Security & Privacy

📖 7 min read 📄 Part 9 of 10

Webhook Notification Service - Security and Privacy

Authentication and Authorization

Webhook Registration Authentication

Methods:

1. API Keys:
POST /api/v1/webhooks
Authorization: Bearer sk_live_abc123def456

Pros: Simple, stateless
Cons: Key theft risk

2. OAuth 2.0:
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

Pros: Standard, secure, token expiration
Cons: More complex

3. Service Accounts:
- Machine-to-machine authentication
- Certificate-based
- No passwords

Recommendation: OAuth 2.0 for users, service accounts for systems

Webhook Delivery Authentication

Customer Endpoint Authentication:

1. HMAC Signature (Recommended):
X-Signature: sha256=abc123def456...

Verification:
expected = HMAC-SHA256(secret, payload)
valid = compare_digest(expected, received_signature)

2. Bearer Token:
Authorization: Bearer customer_token_xyz

3. Basic Auth:
Authorization: Basic base64(username:password)

4. mTLS:
- Client certificate authentication
- Highest security
- Complex setup

Recommendation: HMAC signature for simplicity and security

Signature Generation and Verification

HMAC-SHA256 Implementation

Signature Generation (Service Side):
import hmac
import hashlib

def generate_signature(payload, secret):
    signature = hmac.new(
        secret.encode(),
        payload.encode(),
        hashlib.sha256
    ).hexdigest()
    return f"sha256={signature}"

# Add to webhook request
headers = {
    'X-Signature': generate_signature(payload, webhook.secret),
    'X-Webhook-ID': webhook.id,
    'X-Event-ID': event.id
}

Signature Verification (Customer Side):
def verify_signature(payload, signature, secret):
    expected = generate_signature(payload, secret)
    return hmac.compare_digest(expected, signature)

# Verify in webhook handler
@app.route('/webhooks', methods=['POST'])
def handle_webhook():
    payload = request.get_data(as_text=True)
    signature = request.headers.get('X-Signature')
    secret = get_webhook_secret()
    
    if not verify_signature(payload, signature, secret):
        return 'Invalid signature', 401
    
    process_webhook(request.json)
    return 'OK', 200

Security Best Practices:
- Use constant-time comparison (compare_digest)
- Include timestamp in signature (prevent replay)
- Rotate secrets regularly
- Use strong secrets (32+ characters)

Replay Attack Prevention

Timestamp-Based Protection:
{
  "event_id": "evt_123",
  "timestamp": 1704708000,
  "data": {...}
}

signature = HMAC-SHA256(secret, payload_with_timestamp)

Verification:
def verify_webhook(payload, signature, secret):
    # Verify signature
    if not verify_signature(payload, signature, secret):
        return False
    
    # Check timestamp (reject if >5 minutes old)
    event_time = payload['timestamp']
    if abs(now() - event_time) > 300:
        return False
    
    return True

Benefits:
- Prevents replay attacks
- Time-bound validity
- Simple implementation

Secret Management

Secure Secret Storage

Secret Generation:
import secrets

def generate_webhook_secret():
    return 'whsec_' + secrets.token_urlsafe(32)

Secret Storage:
- Encrypt at rest (AES-256)
- Store in secrets manager (Vault, AWS Secrets Manager)
- Never log secrets
- Mask in API responses

Implementation:
class SecretManager {
    String storeSecret(String webhookId, String secret) {
        // Encrypt secret
        String encrypted = encrypt(secret, masterKey);
        
        // Store in database
        database.execute(
            "UPDATE webhooks SET secret = ? WHERE webhook_id = ?",
            encrypted, webhookId
        );
        
        // Cache encrypted secret
        redis.setex("secret:" + webhookId, 3600, encrypted);
        
        return webhookId;
    }
    
    String getSecret(String webhookId) {
        // Try cache first
        String encrypted = redis.get("secret:" + webhookId);
        
        if (encrypted == null) {
            // Load from database
            encrypted = database.query(
                "SELECT secret FROM webhooks WHERE webhook_id = ?",
                webhookId
            );
            redis.setex("secret:" + webhookId, 3600, encrypted);
        }
        
        // Decrypt and return
        return decrypt(encrypted, masterKey);
    }
}

Secret Rotation:
- Rotate every 90 days
- Support dual secrets during rotation
- Notify customers before rotation
- Automatic rotation process

Network Security

TLS/SSL Enforcement

Requirements:
- Enforce HTTPS for all webhooks
- Reject HTTP endpoints
- Validate SSL certificates
- Support custom CA certificates

Implementation:
class WebhookValidator {
    boolean isValidURL(String url) {
        // Must be HTTPS
        if (!url.startsWith("https://")) {
            throw new InvalidURLException("HTTPS required");
        }
        
        // Validate SSL certificate
        try {
            SSLContext context = SSLContext.getInstance("TLS");
            context.init(null, trustManagers, null);
            
            URL endpoint = new URL(url);
            HttpsURLConnection conn = (HttpsURLConnection) endpoint.openConnection();
            conn.connect();
            conn.disconnect();
            
            return true;
        } catch (SSLException e) {
            throw new InvalidCertificateException("Invalid SSL certificate");
        }
    }
}

Certificate Validation:
- Verify certificate chain
- Check expiration date
- Validate hostname
- Check revocation status (OCSP)

IP Allowlisting

Configuration:
{
  "webhook_id": "wh_123",
  "ip_allowlist": [
    "203.0.113.0/24",
    "198.51.100.42"
  ]
}

Implementation:
class IPFilter {
    boolean isAllowed(String webhookId, String sourceIP) {
        List<String> allowlist = getIPAllowlist(webhookId);
        
        if (allowlist.isEmpty()) {
            return true;  // No restrictions
        }
        
        for (String cidr : allowlist) {
            if (ipInRange(sourceIP, cidr)) {
                return true;
            }
        }
        
        return false;
    }
}

Benefits:
- Additional security layer
- Prevent unauthorized access
- Compliance requirements

Data Privacy

PII in Webhook Payloads

Problem:
Webhook payload may contain PII:
- User emails
- Phone numbers
- Addresses
- Payment details

Solutions:

1. Tokenization:
{
  "event_type": "user.created",
  "data": {
    "user_id": 123,
    "email": "token:email:abc123",  // Tokenized
    "phone": "token:phone:def456"   // Tokenized
  }
}

2. Encryption:
{
  "event_type": "payment.success",
  "data": {
    "payment_id": "pay_123",
    "card_number": "encrypted:AES256:...",
    "cvv": "encrypted:AES256:..."
  }
}

3. Minimal Data:
{
  "event_type": "user.created",
  "data": {
    "user_id": 123,
    "profile_url": "https://api.example.com/users/123"
  }
}
// Customer fetches full data via API

4. Field-Level Permissions:
{
  "webhook_id": "wh_123",
  "allowed_fields": ["user_id", "email"],
  "denied_fields": ["ssn", "credit_card"]
}

Recommendation: Minimal data + API references

GDPR Compliance

Requirements:

1. Right to Access:
   - Export user's webhook data
   - Include all deliveries
   
2. Right to Erasure:
   - Delete user's webhooks
   - Anonymize delivery logs
   
3. Data Minimization:
   - Store only necessary data
   - Minimize retention period
   
4. Consent Management:
   - Track user consent
   - Respect opt-outs

Implementation:
class GDPRService {
    void exportUserData(String userId) {
        // Export webhooks
        List<Webhook> webhooks = getWebhooks(userId);
        
        // Export deliveries
        List<Delivery> deliveries = getDeliveries(userId);
        
        // Export as JSON
        return JSON.stringify({
            webhooks: webhooks,
            deliveries: deliveries
        });
    }
    
    void deleteUserData(String userId) {
        // Delete webhooks
        deleteWebhooks(userId);
        
        // Anonymize deliveries
        anonymizeDeliveries(userId);
        
        // Audit log
        auditLog("user_data_deleted", userId);
    }
}

Audit Logging

Comprehensive Audit Trail

Events to Log:
- Webhook creation/deletion
- Webhook updates
- Delivery attempts
- Delivery failures
- Configuration changes
- Authentication attempts
- Rate limit violations

Log Format:
{
  "timestamp": "2024-01-08T10:00:00Z",
  "event_type": "webhook_delivery",
  "webhook_id": "wh_abc123",
  "delivery_id": "del_xyz789",
  "user_id": "user_123",
  "endpoint_url": "https://customer.example.com/webhooks",
  "status": "success",
  "response_status": 200,
  "response_time_ms": 250,
  "attempt_number": 1,
  "ip_address": "203.0.113.42"
}

Storage:
- Immutable append-only log
- Long retention (1 year+)
- Indexed for fast queries
- Exported to SIEM

Compliance:
- SOC 2 Type II
- ISO 27001
- PCI DSS (for payment webhooks)

Abuse Prevention

Rate Limiting and Throttling

API Rate Limits:
- Free tier: 1,000 webhooks/day
- Pro tier: 100,000 webhooks/day
- Enterprise: Unlimited

Webhook Limits:
- Max 100 webhooks per user
- Max 50 event subscriptions per webhook
- Max 10 MB payload size
- Max 30 second timeout

Implementation:
class AbuseDetector {
    void detectAbuse(String userId) {
        // Check webhook creation rate
        int webhooksToday = getWebhookCount(userId, today());
        if (webhooksToday > 100) {
            alertSecurityTeam("Excessive webhook creation", userId);
            throttleUser(userId);
        }
        
        // Check delivery rate
        int deliveriesToday = getDeliveryCount(userId, today());
        if (deliveriesToday > 1000000) {
            alertSecurityTeam("Excessive deliveries", userId);
            throttleUser(userId);
        }
        
        // Check failure rate
        double failureRate = getFailureRate(userId);
        if (failureRate > 0.5) {
            alertUser(userId, "High webhook failure rate");
        }
    }
}

DDoS Protection

Mitigation Strategies:

1. Rate Limiting:
   - Limit webhook registrations
   - Limit event publishing
   - Throttle excessive traffic

2. IP Reputation:
   - Block known malicious IPs
   - Challenge suspicious IPs
   - Allowlist trusted IPs

3. Payload Validation:
   - Max payload size: 10 MB
   - Validate JSON structure
   - Sanitize inputs

4. Resource Limits:
   - Max concurrent deliveries per user
   - Max queue depth per user
   - Automatic throttling

Incident Response

Security Incident Procedures

1. Detection:
   - Monitor audit logs
   - Alert on suspicious activity
   - Automated threat detection

2. Containment:
   - Disable compromised webhooks
   - Block malicious IPs
   - Revoke compromised secrets

3. Investigation:
   - Analyze audit logs
   - Identify attack vector
   - Assess damage

4. Recovery:
   - Rotate all secrets
   - Restore from backups
   - Apply security patches

5. Post-Mortem:
   - Document incident
   - Update procedures
   - Implement preventive measures

Runbook:
# Disable compromised webhook
UPDATE webhooks SET enabled = FALSE WHERE webhook_id = 'wh_123';

# Rotate secret
UPDATE webhooks SET secret = generate_secret() WHERE webhook_id = 'wh_123';

# Block malicious IP
iptables -A INPUT -s 198.51.100.42 -j DROP

This comprehensive security approach ensures the webhook service is protected against threats while maintaining compliance with privacy regulations.