10 min read

Stripe Integration Security

This document explains the security model of iaptic's Stripe integration and how it protects your subscription data.

Security Layers

1. Access Keys

graph LR
    A[Client] -- Access Key --> B[Iaptic]
    B -- Verify --> C[Subscription]
    C -- Rotate --> D[New Key]
    D -- Return --> A

Access keys are:

  • Generated during checkout
  • Expire after 30 days
  • Automatically rotated
  • Subscription-specific
  • Stored client-side only

2. API Keys

graph LR
    A[Iaptic] -- Secret Key --> B[Stripe API]
    C[Client] -- Public Key --> D[Stripe Elements]

Two types of Stripe keys:

  • Secret key (server-side only)
  • Public key (safe for client-side)

3. Webhook Security

graph LR
    A[Stripe] -- Event --> B[Webhook]
    B -- Verify Signature --> C[Process]
    B -- Invalid --> D[Reject]

Each webhook request:

  • Includes signature header
  • Uses unique signing secret
  • Validates timestamp
  • Verifies payload integrity

Key Management

Access Key Format

// Format: ak_[32 random chars]
const ACCESS_KEY_PATTERN = /^ak_[a-zA-Z0-9]{32}$/;

// Example
const validKey = 'ak_1234567890abcdef1234567890abcdef';

Key Storage

// Client-side (correct)
localStorage.setItem('stripe_access_key', accessKey);

// Server-side (avoid)
user.accessKey = accessKey; // Don't store unencrypted

// Server-side (if needed)
user.accessKey = await encrypt(accessKey);

Key Rotation

Keys are rotated when:

  • Approaching expiration
  • Security requires it
  • Subscription changes
  • Client requests it

Secure Communication

API Requests

// Correct: Send in request body
fetch('/api/check', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ access_key: accessKey })
});

// Incorrect: Don't send in URL
fetch(`/api/check?access_key=${accessKey}`); // Avoid

Webhook Processing

// Verify webhook signature
const event = stripe.webhooks.constructEvent(
  rawBody,
  signature,
  webhookSecret
);

// Always verify managed_by_iaptic flag
if (event.data.object.metadata?.managed_by_iaptic !== 'true') {
  return;
}

Data Protection

Sensitive Data

Never store:

  • Credit card numbers
  • Full card details
  • CVV codes
  • Unencrypted access keys

Metadata Usage

// Safe to store in metadata
{
  "application_username": "user123",
  "features": "premium,api",
  "tier": "enterprise"
}

// Never store in metadata
{
  "access_key": "ak_xyz",     // No!
  "password": "secret",       // No!
  "personal_info": "private"  // No!
}

Security Best Practices

1. Access Key Handling

class AccessKeyManager {
  // Store key securely
  static storeKey(subscriptionId, key) {
    if (!this.isValidKey(key)) return;
    localStorage.setItem(
      `stripe_key_${subscriptionId}`,
      key
    );
  }

  // Validate key format
  static isValidKey(key) {
    return /^ak_[a-zA-Z0-9]{32}$/.test(key);
  }

  // Clear on logout
  static clearKeys() {
    Object.keys(localStorage)
      .filter(k => k.startsWith('stripe_key_'))
      .forEach(k => localStorage.removeItem(k));
  }
}

2. Error Handling

// Don't expose internal errors
try {
  await validateSubscription();
} catch (error) {
  // Log internally
  console.error('Validation failed:', error);
  
  // Return safe message
  throw new Error('Subscription check failed');
}

3. Version Control

// Always specify API version
const stripe = new Stripe(secretKey, {
  apiVersion: '2024-11-20.acacia'
});

Security Checklist

Implementation

  • [ ] Use HTTPS everywhere
  • [ ] Validate all inputs
  • [ ] Handle key rotation
  • [ ] Process webhooks securely
  • [ ] Store keys safely
  • [ ] Use correct API version

Monitoring

  • [ ] Watch for failed webhooks
  • [ ] Monitor key rotations
  • [ ] Track access patterns
  • [ ] Log security events
  • [ ] Alert on anomalies

Maintenance

  • [ ] Update API version
  • [ ] Rotate webhook secrets
  • [ ] Clean expired keys
  • [ ] Audit metadata
  • [ ] Review permissions

Common Vulnerabilities

1. Key Exposure

  • Logging access keys
  • Storing server-side
  • Sending in URLs
  • Committing to code

2. Webhook Security

  • Missing signature verification
  • Old API versions
  • Replay attacks
  • Race conditions

3. Access Control

  • Incorrect entitlement strategy
  • Missing validation
  • Improper key rotation
  • Metadata leaks

Authentication Flow

sequenceDiagram
    participant C as Client
    participant I as Iaptic
    participant S as Stripe

    C->>I: Create Checkout Session
    I->>S: Create Session
    S-->>I: Session + Access Key
    I-->>C: Redirect URL + Key
    C->>S: Complete Payment
    S->>I: Webhook Event
    I->>I: Validate Key
    I-->>C: Confirmation

Access Key Rotation

stateDiagram-v2
    [*] --> Active
    Active --> Rotating: Update
    Rotating --> Transitioning: New Key
    Transitioning --> Active: Complete
    Transitioning --> Failed: Error
    Failed --> Active: Retry