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
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
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¶
- OWASP Mobile Security Testing Guide
- Cloudflare Workers KV Documentation
- iOS Keychain Services
- Rate Limiting Best Practices
✅ 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