Skip to content

Cloudflare Worker Security Guide

🚨 The Problem

Your iOS app WILL be reverse engineered. Attackers can: - Extract all hardcoded strings, URLs, and API keys - Monitor network traffic with MitM proxies - Decompile Swift code to understand request patterns - Automate API abuse to drain your OpenAI credits

✅ The Solution

Server-side enforcement - Never trust the client. Your Cloudflare Workers must: 1. Authenticate each device independently 2. Enforce per-device rate limits 3. Track global usage to prevent mass abuse 4. Log suspicious patterns for monitoring


🛡️ Security Architecture

How It Works

┌─────────────┐
│  iOS App    │
│             │
│ 1. Generate │ On first launch, creates unique device ID
│    Device   │ Stores in Keychain (persists across app updates)
│    ID       │
└──────┬──────┘
       │ 2. Sign requests with device credentials
       │    X-Device-ID: "ABC-123-XYZ"
       │    X-Device-Secret: hash(deviceId + salt)
┌──────────────────────────┐
│  Cloudflare Worker       │
│                          │
│  3. Verify signature     │ Validates device secret matches
│  4. Check rate limits    │ Per device: 5/hour (free), 50/hour (paid)
│  5. Check daily quota    │ Per device: 10/day (free), 160/day (paid)
│  6. Check global limits  │ Total: 1000 requests/hour across all users
│                          │
│  IF ALL PASS:            │
│  7. Call OpenAI API      │ Using server-side API key (never exposed)
│  8. Increment counters   │ Track usage in Cloudflare KV
│  9. Return result        │
└──────────────────────────┘

Why This Works

Even if an attacker reverse engineers your app: - ✅ They can only abuse THEIR OWN device quota (10/day for free users) - ✅ They cannot generate valid signatures for fake device IDs (salt is server-side only) - ✅ Global rate limits prevent mass attacks with multiple devices - ✅ OpenAI API key is never exposed (stays in Cloudflare Worker)

Cost Protection: - Free user abusing 10 requests/day = $0.02/day max damage - Global limit of 1000/hour = ~$10/hour max damage even in worst case - Monitoring alerts you to unusual patterns


📋 Implementation Checklist

Step 1: Set Up Cloudflare Worker

1.1 Create KV Namespaces

wrangler kv:namespace create "RATE_LIMIT"
wrangler kv:namespace create "SUBSCRIPTIONS"

1.2 Add Environment Variables

# In Cloudflare Dashboard → Workers → Your Worker → Settings → Variables

# Secrets (encrypted):
OPENAI_API_KEY = "sk-your-actual-key"
DEVICE_SALT = "random-string-generate-with-openssl-rand-base64-32"

# KV Namespace Bindings:
RATE_LIMIT = your-kv-namespace-id
SUBSCRIPTIONS = your-kv-namespace-id

1.3 Deploy Worker

wrangler publish cloudflare-worker-secure-example.js

Step 2: Update iOS App

2.1 Add DeviceAuthService - Copy DeviceAuthService-EXAMPLE.swift to your project - CRITICAL: Update deviceSaltHint to match Cloudflare Worker's DEVICE_SALT

2.2 Update OpenAIService.swift

func extractFoodInformation(...) async throws -> FoodInfo {
    // Add device auth headers
    var request = URLRequest(url: url)
    let authHeaders = DeviceAuthService.shared.getAuthHeaders()
    for (key, value) in authHeaders {
        request.setValue(value, forHTTPHeaderField: key)
    }

    // Rest of your code...
}

2.3 Handle Rate Limit Errors

if httpResponse.statusCode == 429 {
    // Show user-friendly error message
    throw AIUsageError.limitReached
}

Step 3: Configure Subscription Tiers

When user purchases subscription, update Cloudflare KV:

// After successful StoreKit purchase:
let deviceId = DeviceAuthService.shared.getDeviceId()

// Call your admin endpoint (authenticated separately)
let subscriptionData = [
    "plan": "pro",  // basic, pro, premium, ultimate
    "expiresAt": "2025-12-31T23:59:59Z"
]

// Worker stores in KV:
// await env.SUBSCRIPTIONS.put(deviceId, JSON.stringify(subscriptionData))

Step 4: Monitoring & Alerts

4.1 Set Up Cloudflare Analytics - Enable Worker Analytics in Cloudflare Dashboard - Monitor request volume, error rates, response times

4.2 Alert Thresholds

// In your worker, log suspicious patterns:

if (deviceUsage > 40 in last hour) {
  console.warn('Possible abuse from device:', deviceId);
}

if (globalUsage > 800 in last hour) {
  console.error('ALERT: Approaching global rate limit!');
}

4.3 External Monitoring (Optional) - Use Sentry/Datadog to track worker errors - Set up email alerts for rate limit violations - Monitor OpenAI API costs in OpenAI dashboard


🔒 Additional Security Measures

Certificate Pinning (Advanced)

Prevent MitM attacks by only accepting your Cloudflare Worker's certificate:

class SecureNetworkManager: NSObject, URLSessionDelegate {
    func urlSession(_ session: URLSession,
                    didReceive challenge: URLAuthenticationChallenge,
                    completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {

        guard challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust,
              let serverTrust = challenge.protectionSpace.serverTrust else {
            completionHandler(.cancelAuthenticationChallenge, nil)
            return
        }

        // Get your Cloudflare Worker's certificate:
        // openssl s_client -connect your-worker.workers.dev:443 -showcerts
        let pinnedCertData = Data(base64Encoded: "YOUR_CERT_BASE64")!

        // Validate certificate matches
        // Implementation details: https://owasp.org/www-community/controls/Certificate_and_Public_Key_Pinning

        completionHandler(.useCredential, URLCredential(trust: serverTrust))
    }
}

Jailbreak Detection

Refuse to run on jailbroken devices (where attackers have root access):

func isJailbroken() -> Bool {
    #if targetEnvironment(simulator)
    return false
    #else
    let paths = [
        "/Applications/Cydia.app",
        "/Library/MobileSubstrate/MobileSubstrate.dylib",
        "/bin/bash",
        "/usr/sbin/sshd",
        "/etc/apt"
    ]

    for path in paths {
        if FileManager.default.fileExists(atPath: path) {
            return true
        }
    }

    return false
    #endif
}

// In app launch:
if isJailbroken() {
    // Show warning or disable AI features
}

💰 Cost Analysis

Worst Case Scenarios

Scenario 1: Single Attacker - Reverse engineers app, extracts device ID - Can only abuse their own device quota: 10 requests/day - Cost: ~$0.02/day (negligible)

Scenario 2: Mass Attack with 100 Devices - Attacker creates 100 fake device IDs - Each device: 10 requests/day = 1000 requests/day total - Global limit: 1000/hour = max 24,000/day - Cost: ~$0.50/day (acceptable)

Scenario 3: Compromised Worker Salt - If attacker gets DEVICE_SALT, they can create unlimited valid devices - Global limit still caps at 1000/hour = $10/hour max - Mitigation: Rotate salt monthly, invalidate old device secrets

Real-World Protection

With proper implementation: - Free tier abuse: Limited to 10 requests/device/day - Paid tier abuse: User paid you for the quota anyway - Mass attacks: Capped by global limits - Total risk: <$50/month even in extreme scenarios


🔄 Maintenance

Monthly Tasks

  • [ ] Review Cloudflare Worker analytics
  • [ ] Check OpenAI API usage vs. expected
  • [ ] Rotate DEVICE_SALT if suspicious activity detected
  • [ ] Update rate limits based on actual usage patterns

Incident Response

If you detect abuse: 1. Check Cloudflare Worker logs for offending device IDs 2. Add device ID to blocklist in Worker 3. Investigate root cause (leaked credentials, etc.) 4. Rotate salts/secrets if needed


📚 References


✅ Production Readiness Checklist

Before launching: - [ ] Deploy secure Cloudflare Worker with all layers enabled - [ ] Set DEVICE_SALT and OPENAI_API_KEY in Cloudflare secrets - [ ] Create KV namespaces for rate limiting and subscriptions - [ ] Update iOS app with DeviceAuthService - [ ] Add device auth headers to all API calls - [ ] Test rate limiting with sandbox account - [ ] Set up monitoring alerts for abuse patterns - [ ] Document incident response procedures - [ ] Test with jailbroken device (should fail gracefully) - [ ] Verify OpenAI costs are within expected range

Expected Results: - ✅ Each device limited to 10 free requests/day - ✅ Paid subscribers get their purchased quota - ✅ Global abuse limited to <$50/month even in worst case - ✅ You sleep well knowing your OpenAI credits are protected