Back to Skills
💼
VerifiedSimple🥈silver💼Business Operations

Billing Security Auditor

Audit billing code for security vulnerabilities, validate inputs, and ensure payment security best practices.

Verified
Version1.0.0
AuthorID8Labs
LicenseMIT
Published1/6/2026
View on GitHub

Skill Content

# Billing Security Auditor Skill

Audit billing code for security vulnerabilities, validate inputs, and ensure payment security best practices.

## When to Use This Skill

Use before:
- Deploying payment code to production
- Creating pull requests with billing changes
- Security reviews
- After making any changes to payment endpoints

## Security Checklist

### 1. Input Validation

**Check ALL user inputs are validated**:

✅ `/api/billing/checkout`:
- [ ] `priceId` validated against known price IDs
- [ ] `returnUrl` validated against whitelist
- [ ] No direct use of user input in Stripe API calls
- [ ] All parameters have type checking

✅ `/api/billing/portal`:
- [ ] `returnUrl` validated against whitelist
- [ ] User authentication verified
- [ ] Customer ID retrieved from database, not user input

✅ `/api/webhooks/stripe`:
- [ ] Signature verification BEFORE processing
- [ ] Event type checked in whitelist
- [ ] No SQL injection vectors in metadata
- [ ] Metadata fields sanitized

### 2. Authentication & Authorization

**Every billing endpoint must**:
- [ ] Verify user is authenticated (JWT/session)
- [ ] Verify user owns the subscription (RLS or manual check)
- [ ] Use service role for database writes (not user credentials)
- [ ] Never trust client-side tier/subscription data

**Check RLS policies**:
```sql
-- Users can only SELECT own subscription
SELECT policy_name FROM pg_policies
WHERE tablename = 'subscriptions' AND cmd = 'SELECT';

-- Service role can manage all subscriptions
-- Users CANNOT INSERT/UPDATE/DELETE subscriptions
```

### 3. Secrets Management

**Audit for exposed secrets**:

❌ **Never do this**:
```typescript
// BAD: API key in code
const stripe = new Stripe('sk_live_abc123');

// BAD: Logged to console
console.log('Stripe key:', process.env.STRIPE_SECRET_KEY);

// BAD: Returned to client
return { webhookSecret: process.env.STRIPE_WEBHOOK_SECRET };
```

✅ **Do this**:
```typescript
// GOOD: From environment variable
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!);

// GOOD: Logged without value
console.log('Stripe configured:', !!process.env.STRIPE_SECRET_KEY);

// GOOD: Never sent to client
// Server-side only
```

**Check for secret leakage**:
```bash
# Search for potential secret exposure
grep -r "STRIPE_SECRET_KEY" src/
grep -r "WEBHOOK_SECRET" src/
grep -r "console.log.*stripe" src/
```

### 4. Injection Attack Prevention

**SQL Injection**:
- [ ] All database queries use parameterized queries
- [ ] No string concatenation in SQL
- [ ] User input never directly in WHERE clauses

**Command Injection**:
- [ ] No dynamic code execution with user input
- [ ] No shell commands with user input
- [ ] All file paths validated

### 5. Rate Limiting & DoS Prevention

**Check rate limits exist**:
- [ ] `/api/billing/checkout` → 5 req/min per user
- [ ] `/api/billing/portal` → 10 req/min per user
- [ ] `/api/webhooks/stripe` → (Stripe handles this)

**Test rate limiting**:
```bash
# Make 6 rapid requests
for i in {1..6}; do
  curl -X POST http://localhost:3000/api/billing/checkout \
    -H "Authorization: Bearer $TOKEN" \
    -H "Content-Type: application/json" \
    -d '{"priceId":"price_test","returnUrl":"http://localhost:3000/billing"}'
  echo "Request $i"
done

# 6th request should return 429
```

### 6. OWASP Top 10 Review

**A01: Broken Access Control**
- [ ] Users can't access other users' subscriptions
- [ ] Users can't modify their own subscription tier directly
- [ ] RLS policies enforce ownership

**A02: Cryptographic Failures**
- [ ] Webhook signatures verified with cryptographic function
- [ ] HTTPS enforced on all endpoints
- [ ] No sensitive data in URLs/query params

**A03: Injection**
- [ ] See section 4 above

**A04: Insecure Design**
- [ ] No security bypasses (e.g., "admin=true" param)
- [ ] Upgrade flow requires payment, can't be bypassed
- [ ] Tier verification on every API request

**A05: Security Misconfiguration**
- [ ] Error messages don't leak stack traces
- [ ] Dev tools (Stripe test mode) not in production
- [ ] Environment variables properly scoped

**A07: Identification & Authentication Failures**
- [ ] Sessions expire appropriately
- [ ] No weak authentication (API keys in URLs)
- [ ] Multi-factor encouraged for high-value accounts

**A08: Software & Data Integrity Failures**
- [ ] Webhook signatures prevent tampering
- [ ] Database constraints enforce data integrity
- [ ] No unsigned webhooks processed

**A09: Security Logging & Monitoring Failures**
- [ ] Failed payment attempts logged
- [ ] Webhook failures logged
- [ ] Unusual activity patterns detectable

**A10: Server-Side Request Forgery (SSRF)**
- [ ] No user-controlled URLs fetched by server
- [ ] Webhook URLs validated (Stripe sends to us, we don't fetch)

### 7. Payment-Specific Security

**PCI Compliance**:
- [ ] No credit card data stored
- [ ] No credit card data logged
- [ ] All payment processing via Stripe (tokenized)

**Webhook Security**:
- [ ] Signature verification before processing
- [ ] Idempotency keys used (prevent duplicate charges)
- [ ] Replay attack prevention (timestamp validation)

**Subscription Manipulation**:
- [ ] Users can't create subscriptions without payment
- [ ] Users can't upgrade tier without checkout
- [ ] Users can't fake webhook events

### 8. Error Handling Security

**Check error messages don't leak info**:

❌ **Bad**:
```typescript
catch (error) {
  return NextResponse.json({ error: error.message }, { status: 500 });
  // Might leak: "Database connection failed at 192.168.1.5:5432"
}
```

✅ **Good**:
```typescript
catch (error) {
  console.error('Checkout error:', error); // Log internally
  return NextResponse.json({
    error: 'Failed to create checkout session. Please try again.',
    code: 'CHECKOUT_ERROR'
  }, { status: 500 });
}
```

### 9. Automated Security Scan

Run these commands:

```bash
# Dependency vulnerabilities
npm audit

# TypeScript strict mode (catches some issues)
npm run type-check

# ESLint security plugin
npm run lint
```

### 10. Manual Code Review Checklist

For each file in `/src/app/api/billing/` and `/src/lib/billing/`:

- [ ] Read through line by line
- [ ] Question every user input
- [ ] Verify every database query
- [ ] Check every API call
- [ ] Test error paths
- [ ] Verify auth on every endpoint

## Conclusion

Security is not a checkbox - it's a mindset. Assume attackers will try:
- Forged webhooks
- Price manipulation
- Tier escalation
- Account takeover
- Data exfiltration

Defend accordingly.

Tags

Statistics

Installs0
Views0

Related Skills