This document outlines critical security fixes and enhancements made to functions/utils/security.ts following a comprehensive security audit.
Status: Documented as known limitation Severity: HIGH Impact: Concurrent requests can bypass rate limits
Decision:
- Documented the race condition in code comments
- Added TODO to fix with Durable Objects in Phase 3
- Acceptable risk for current threat model (low-impact API abuse)
- Mitigation: IP blocking for detected abuse patterns
Future Fix: Phase 3 - Durable Objects with atomic operations
Status: FIXED Severity: HIGH Impact: Attackers could bypass IP-based protections
Changes:
- Removed fallback to
X-Forwarded-ForandX-Real-IPheaders - Now ONLY trusts
CF-Connecting-IP(set by Cloudflare, cannot be spoofed) - Added IP format validation (IPv4 and IPv6)
- Returns 'unknown' for non-Cloudflare deployments
Code:
// BEFORE: Vulnerable to header spoofing
const xForwardedFor = request.headers.get('X-Forwarded-For');
if (xForwardedFor) {
return xForwardedFor.split(',')[0].trim(); // Can be spoofed
}
// AFTER: Only trust Cloudflare header
const cfIP = request.headers.get('CF-Connecting-IP');
if (cfIP && isValidIP(cfIP)) {
return cfIP; // Cannot be spoofed
}
return 'unknown';Status: FULLY IMPLEMENTED Severity: MEDIUM Impact: Any website could make requests and exfiltrate data
Changes:
- Removed default
'*'origin fromaddCORSHeaders() - Added
validateOrigin()function with 3-tier priority system - Default allowlist for development (localhost ports)
- Production origins configurable via
ALLOWED_ORIGINSenvironment variable - Integrated into all 5 public API endpoints (stats, threats, search, sources, threat/[id])
- Added
Access-Control-Allow-Credentials: truefor non-wildcard origins
Code:
// BEFORE: Dangerous default
export function addCORSHeaders(
response: Response,
origin: string = '*', // ⚠️ VULNERABLE
...
)
// AFTER: Explicit origin required with 3-tier priority
export function addCORSHeaders(
response: Response,
origin: string, // ✅ No default - must validate first
...
)
// New validation function with production configuration support
export function validateOrigin(
origin: string | null,
env?: Env,
allowedOrigins?: string[]
): string | null {
if (!origin) return null;
// Priority 1: Explicit override (for tests)
if (allowedOrigins) {
return allowedOrigins.includes(origin) ? origin : null;
}
// Priority 2: Environment variable (for production)
if (env?.ALLOWED_ORIGINS) {
const envOrigins = env.ALLOWED_ORIGINS.split(',').map(o => o.trim()).filter(Boolean);
return envOrigins.includes(origin) ? origin : null;
}
// Priority 3: Defaults (for development)
return DEFAULT_ALLOWED_ORIGINS.includes(origin) ? origin : null;
}Production Configuration:
# Set in Cloudflare Dashboard → Workers → Settings → Variables
ALLOWED_ORIGINS="https://yourdomain.com,https://app.yourdomain.com"Status: IMPLEMENTED Features:
- Check if IP is blocked:
isIPBlocked(env, ip) - Block IP temporarily or permanently:
blockIP(env, ip, duration, reason) - Unblock IP:
unblockIP(env, ip) - Integrated into
securityMiddleware()(checked first, before rate limiting)
Usage:
// Block IP for 1 hour
await blockIP(env, '1.2.3.4', 3600, 'rate_limit_abuse');
// Permanent block
await blockIP(env, '1.2.3.4', 0, 'malicious_activity');
// Check in middleware
if (await isIPBlocked(env, ip)) {
return blockedIPResponse('Your IP has been blocked for abuse');
}Status: IMPLEMENTED Added headers:
Strict-Transport-Security(HSTS) - Force HTTPS, 1 year, include subdomainsCross-Origin-Embedder-Policy: require-corpCross-Origin-Opener-Policy: same-originCross-Origin-Resource-Policy: same-origin
Comprehensive CSP for HTML:
default-src 'self';
script-src 'self' 'unsafe-inline' 'unsafe-eval'; // TODO: Remove unsafe-* in production
style-src 'self' 'unsafe-inline';
img-src 'self' data: https:;
font-src 'self' data:;
connect-src 'self' https://api.cloudflare.com;
frame-ancestors 'none';
base-uri 'self';
form-action 'self'
Strict CSP for JSON APIs:
default-src 'none'
Status: IMPLEMENTED
Function: isValidIP(ip: string): boolean
- Validates IPv4 format (xxx.xxx.xxx.xxx, octets 0-255)
- Validates IPv6 format (simplified regex)
- Prevents malformed IP strings from being used
The following tests need updates to match new security requirements:
addSecurityHeaders()- HTML responses now have comprehensive CSPaddCORSHeaders()- No default wildcard, requires explicit originhandleCORSPreflight()- No default wildcard, must validate origin firstsecurityMiddleware()- Now checks IP blocklist before rate limiting
CORS Usage (Before):
// OLD: Implicit wildcard (vulnerable)
const response = addCORSHeaders(apiResponse);CORS Usage (After):
// NEW: Explicit origin validation
const requestOrigin = request.headers.get('Origin');
const validatedOrigin = validateOrigin(requestOrigin);
if (validatedOrigin) {
const response = addCORSHeaders(apiResponse, validatedOrigin);
}-
Implement rate limiting using KV for API endpoints✅ (with known race condition) -
Add CORS configuration with domain allowlist✅ (integrated + production config via env var) - Create request validation middleware
- Add input sanitization for search queries (basic implementation exists)
-
Implement CSP headers for frontend✅ - Add API key rotation mechanism
-
Create IP-based rate limiting for abuse prevention✅
-
Fail Open vs Fail Closed:
- Rate limiting: Fail open (allow request if check fails)
- IP blocking: Fail open (don't block if check fails)
- Rationale: Availability over perfect security for non-critical APIs
-
Defense in Depth:
- IP blocking → Rate limiting → Input validation → Output sanitization
- Multiple layers of protection
-
Least Privilege:
- CORS restricted to allowlist by default
- No wildcard '*' unless explicitly set
-
Security by Default:
- All responses get security headers
- HTML responses get comprehensive CSP
- HSTS enforces HTTPS
-
Audit Logging:
- IP blocks are logged with reason
- Rate limit failures could be logged (enhancement needed)
| Issue | Before | After | Residual Risk |
|---|---|---|---|
| Rate limit bypass | HIGH | MEDIUM | Race condition remains (Phase 3 fix) |
| Header spoofing | HIGH | LOW | Only on non-Cloudflare deployments |
| CORS abuse | MEDIUM | LOW | Wildcard must be explicit |
| IP blocking | N/A | LOW | Fail-open design acceptable |
| CSP protection | MEDIUM | LOW | HTML responses now protected |
| HTTPS enforcement | MEDIUM | LOW | HSTS enforces HTTPS |
- Update tests to match new security requirements
- Integrate CORS validation into all API endpoints
- Add API key authentication to sensitive endpoints
- Implement request validation middleware
- Phase 3: Replace rate limiting with Durable Objects for atomic operations
- OWASP Secure Headers Project
- Cloudflare Workers Security Best Practices
- MDN CORS Documentation
- TODO.md Phase 1.2 - Security Enhancements
- TODO.md Phase 3.1 - Durable Objects (rate limiting fix)