Skip to main content

Overview

Security is paramount when integrating third-party services. This guide covers security best practices to protect your users and data.

API Key Security

Storage

DO:
# .env file (never commit!)
STICKER_API_KEY=sk_live_abc123...
DON’T:
// NEVER hardcode keys
const apiKey = 'sk_live_abc123...'; // ❌
Use dedicated secrets management for production:
  • AWS Secrets Manager
  • HashiCorp Vault
  • Azure Key Vault
  • Google Cloud Secret Manager
// Example with AWS Secrets Manager
const AWS = require('aws-sdk');
const secretsManager = new AWS.SecretsManager();

async function getAPIKey() {
  const secret = await secretsManager.getSecretValue({
    SecretId: 'sticker/api-key'
  }).promise();
  
  return JSON.parse(secret.SecretString).apiKey;
}
Rotate API keys if compromised:
  1. Contact Sticker support immediately
  2. We’ll issue a new key
  3. Update key in secrets manager
  4. Deploy configuration update
  5. Old key is invalidated

Access Control

Limit who can access API keys:
// Use least privilege principle
const apiKey = process.env.STICKER_API_KEY;

// Only backend services should have access
if (typeof window !== 'undefined') {
  throw new Error('API key cannot be accessed in browser');
}
Never expose API keys in:
  • Client-side JavaScript
  • Mobile app code
  • Public repositories
  • Log files
  • Error messages
  • URLs or query parameters

Request Security

HTTPS Only

Always use HTTPS for API requests:
// Good
const API_URL = 'https://api.usesticker.com/v1';

// Bad - never use HTTP
const API_URL = 'http://api.usesticker.com/v1'; // ❌

Input Validation

Validate all inputs before sending to API:
function validateOrganization(org) {
  const errors = [];
  
  // Email validation
  if (!org.email || !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(org.email)) {
    errors.push('Invalid email format');
  }
  
  // Required fields
  if (!org.name || org.name.trim().length === 0) {
    errors.push('Organization name is required');
  }
  
  // State validation
  const validStates = ['AL', 'AK', 'AZ', /* ... all states */];
  org.shippingLocations?.forEach((loc, idx) => {
    if (!loc.address.province) {
      errors.push(`Address ${idx + 1} is missing state`);
    }
  });
  
  return errors;
}

Rate Limiting

Implement client-side rate limiting:
class RateLimiter {
  constructor(maxRequests, windowMs) {
    this.maxRequests = maxRequests;
    this.windowMs = windowMs;
    this.requests = [];
  }

  async throttle() {
    const now = Date.now();
    
    // Remove old requests outside window
    this.requests = this.requests.filter(
      time => now - time < this.windowMs
    );
    
    // Check if at limit
    if (this.requests.length >= this.maxRequests) {
      const oldestRequest = this.requests[0];
      const waitTime = this.windowMs - (now - oldestRequest);
      await new Promise(resolve => setTimeout(resolve, waitTime));
      return this.throttle();
    }
    
    // Add this request
    this.requests.push(now);
  }
}

// Usage
const limiter = new RateLimiter(100, 60000); // 100 per minute

async function callAPI(endpoint, data) {
  await limiter.throttle();
  return fetch(endpoint, data);
}

iframe Security

Sandbox Attribute

Use restrictive sandbox permissions:
<iframe
  src="https://shop.usesticker.com/embedded/{partner_id}?session_key=..."
  sandbox="allow-same-origin allow-scripts allow-forms allow-popups allow-popups-to-escape-sandbox allow-top-navigation-by-user-activation"
  allow="payment; publickey-credentials-get; fullscreen"
/>
Required permissions:
  • allow-same-origin - Required for authentication cookies
  • allow-scripts - Required for functionality
  • allow-forms - Required for search and checkout
  • allow-popups - Required for payment windows
  • allow-popups-to-escape-sandbox - Required for Stripe
  • allow-top-navigation-by-user-activation - Required for redirects
Avoid unless necessary:
  • allow-top-navigation (without user activation) - Can redirect parent unexpectedly
  • allow-modals - Can show alerts in parent

Content Security Policy

Configure CSP headers to allow Sticker iframe:
Content-Security-Policy:
  default-src 'self';
  frame-src https://shop.usesticker.com https://*.usesticker.com;
  connect-src 'self' https://api.usesticker.com;

Session Token Security

Token Properties

Session tokens are designed with security in mind:
PropertyDescription
Single-useInvalidated after first authentication
Short-livedExpire after 5 minutes
Cryptographic64-character random hex (256 bits of entropy)
User-boundTied to specific user profile
Partner-boundOnly valid for your partner iframe URL

Token Transmission

Safely pass tokens to iframe:
// Good - token in URL parameter (over HTTPS)
const iframeSrc = `https://shop.usesticker.com/embedded/${partnerId}?session_key=${token}`;

// Bad - never put in localStorage accessible to other scripts
localStorage.setItem('sticker_token', token); // ❌

Token Lifecycle

// Generate fresh token for each session
async function openSuppliesModule(userId) {
  // 1. Generate new token
  const { session_key, iframe_embed_url, expires_at } = await handshake(userId);
  
  // 2. Use immediately (don't store)
  renderIframe(iframe_embed_url);
  
  // 3. Token is consumed on first use
  // 4. If user closes and reopens, generate new token
}

Data Security

PII Handling

Only send necessary data to Sticker:
function prepareUserData(user) {
  // Only send necessary fields
  return {
    firstName: user.firstName,
    lastName: user.lastName,
    email: user.email,
    phoneNumber: user.phone  // Optional
    // Don't send: SSN, DOB, medical records, passwords, etc.
  };
}

Data Encryption

All data is protected:
  • ✅ All API calls over HTTPS (TLS 1.3)
  • ✅ Data encrypted at rest
  • ✅ Session tokens are cryptographically random
  • ✅ No passwords stored or transmitted

Security Monitoring

Log Security Events

Track security-relevant events:
function logSecurityEvent(event, severity, details) {
  const securityLog = {
    timestamp: new Date().toISOString(),
    event: event,
    severity: severity,
    details: details
  };
  
  // Send to your logging system
  logger.log(securityLog);
  
  // Alert on critical events
  if (severity === 'critical') {
    alertSecurityTeam(securityLog);
  }
}

// Example usage
logSecurityEvent('api_key_exposed', 'critical', {
  message: 'API key found in client-side code',
  file: 'app.js'
});

Monitor for Anomalies

Detect suspicious activity:
class AnomalyDetector {
  trackRequest(userId, success) {
    // Track error rates per user
    const userErrors = this.getErrorCount(userId);
    
    if (!success) {
      this.incrementErrorCount(userId);
      
      // Alert on high error rate
      if (userErrors > 10) {
        this.alert('High error rate', { userId, errors: userErrors });
      }
    }
  }
}

Incident Response

If API Key is Compromised

  1. Contact Sticker immediately
  2. We will revoke the key immediately
  3. We’ll issue a new key within minutes
  4. Update your systems with the new key
  5. Review logs for any unauthorized access

Emergency Contacts

Security Checklist

1

API Keys

  • Keys stored in environment variables/secrets manager
  • No keys in source code or version control
  • Different keys for sandbox and production
  • Access limited to backend services only
2

Requests

  • All requests over HTTPS
  • Input validation before API calls
  • Rate limiting implemented
  • Error messages don’t leak sensitive data
3

iframe

  • Sandbox attribute configured correctly
  • CSP headers allow Sticker domains
  • HTTPS required for parent page
4

Sessions

  • Fresh token generated for each session
  • Tokens never cached or stored
  • Expired/failed tokens handled gracefully
5

Monitoring

  • Security events logged
  • Anomaly detection active
  • Incident response plan documented

Resources