--- id: secondsky/claude-skills/cloudflare-workers-security version: "85e7999f" license: MIT install: manual updated: 2026-07-25 --- # cloudflare-workers-security — Cloudflare Workers Security delivers production patterns for protecting APIs and Workers through authentication verification, request rate limiting, input validation, and security headers. It covers JWT and API key validation, CORS configuration, and defends against common vulnerabilities like injection, XSS, and unauthorized access. Publisher: secondsky · Stars: 196 · Updated: 2026-07-25 Install (manual): `git clone https://github.com/secondsky/claude-skills` ## SKILL.md # Cloudflare Workers Security Comprehensive security patterns for protecting Workers and APIs. ## Quick Security Checklist ```typescript // 1. Validate all input const validated = schema.parse(await request.json()); // 2. Authenticate requests const user = await verifyToken(request.headers.get('Authorization')); if (!user) return new Response('Unauthorized', { status: 401 }); // 3. Rate limit const limited = await rateLimiter.check(clientIP); if (!limited.allowed) return new Response('Too Many Requests', { status: 429 }); // 4. Add security headers response.headers.set('X-Content-Type-Options', 'nosniff'); response.headers.set('X-Frame-Options', 'DENY'); // 5. Use HTTPS-only cookies headers.set('Set-Cookie', 'session=xxx; Secure; HttpOnly; SameSite=Strict'); ``` ## Critical Rules 1. **Never trust client input** - Validate and sanitize everything 2. **Use secure secrets** - Store in Wrangler secrets, never in code 3. **Implement rate limiting** - Protect against abuse 4. **Set security headers** - Prevent common attacks 5. **Use CORS properly** - Don't use `*` in production ## Top 10 Security Errors | Vulnerability | Symptom | Prevention | |---------------|---------|------------| | Missing auth | Unauthorized access | Verify tokens on every request | | SQL injection | Data breach | Use parameterized queries with D1 | | XSS | Script injection | Sanitize output, set CSP | | CORS misconfiguration | Blocked requests or open access | Configure specific origins | | Secrets in code | Exposed credentials | Use `wrangler secret` | | Missing rate limits | DoS vulnerability | Implement per-IP limits | | Weak tokens | Session hijacking | Use crypto.subtle for signing | | Missing HTTPS | Data interception | Enforce HTTPS redirects | | Insecure headers | Clickjacking, MIME attacks | Set security headers | | Excessive permissions | Blast radius | Principle of least privilege | ## Authentication Patterns ### JWT Verification ```typescript async function verifyJWT(token: string, secret: string): Promise<{ valid: boolean; payload?: unknown }> { try { const [headerB64, payloadB64, signatureB64] = token.split('.'); // Verify signature const key = await crypto.subtle.importKey( 'raw', new TextEncoder().encode(secret), { name: 'HMAC', hash: 'SHA-256' }, false, ['verify'] ); const signature = Uint8Array.from(atob(signatureB64.replace(/-/g, '+').replace(/_/g, '/')), c => c.charCodeAt(0)); const data = new TextEncoder().encode(`${headerB64}.${payloadB64}`); const valid = await crypto.subtle.verify('HMAC', key, signature, data); if (!valid) return { valid: false }; // Decode payload const payload = JSON.parse(atob(payloadB64.replace(/-/g, '+').replace(/_/g, '/'))); // Check expiration if (payload.exp && Date.now() / 1000 > payload.exp) { return { valid: false }; } return { valid: true, payload }; } catch { return { valid: false }; } } ``` ### API Key Validation ```typescript async function validateApiKey( request: Request, env: Env ): Promise<{ valid: boolean; clientId?: string }> { const apiKey = request.headers.get('X-API-Key'); if (!apiKey) return { valid: false }; // Hash the key for lookup (never store plain keys) const keyHash = await sha256(apiKey); // Lookup in KV or D1 const client = await env.KV.get(`apikey:${keyHash}`, 'json'); if (!client) return { valid: false }; return { valid: true, clientId: client.id }; } async function sha256(str: string): Promise { const buffer = await crypto.subtle.digest('SHA-256', new TextEncoder().encode(str)); return [...new Uint8Array(buffer)].map(b => b.toString(16).padStart(2, '0')).join(''); } ``` ## Input Validation ### With Zod ```typescript import { z } from 'zod'; const UserSchema = z.object({ name: z.string().min(1).max(100), email: z.string().email(), age: z.number().int().min(0).max(150).optional(), }); async function handleCreate(request: Request): Promise { try { const body = await request.json(); const user = UserSchema.parse(body); // Safe to use validated data return Response.json({ success: true, user }); } catch (error) { if (error instanceof z.ZodError) { return Response.json({ error: 'Validation failed', details: error.errors }, { status: 400 }); } throw error; } } ``` ## Security Headers ```typescript function addSecurityHeaders(response: Response): Response { const headers = new Headers(response.headers); // Prevent MIME type sniffing headers.set('X-Content-Type-Options', 'nosniff'); // Prevent clickjacking headers.set('X-Frame-Options', 'DENY'); // XSS protection headers.set('X-XSS-Protection', '1; mode=block'); // Content Security Policy headers.set('Content-Security-Policy', "default-src 'self'; script-src 'self'"); // HSTS headers.set('Strict-Transport-Security', 'max-age=31536000; includeSubDomains'); // Referrer policy headers.set('Referrer-Policy', 'strict-origin-when-cross-origin'); return new Response(response.body, { status: response.status, headers }); } ``` ## CORS Configuration ```typescript const ALLOWED_ORIGINS = ['https://app.example.com', 'https://admin.example.com']; function handleCORS(request: Request, response: Response): Response { const origin = request.headers.get('Origin'); if (!origin || !ALLOWED_ORIGINS.includes(origin)) { return response; // No CORS headers } const headers = new Headers(response.headers); headers.set('Access-Control-Allow-Origin', origin); headers.set('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS'); headers.set('Access-Control-Allow-Headers', 'Content-Type, Authorization'); headers.set('Access-Control-Allow-Credentials', 'true'); headers.set('Access-Control-Max-Age', '86400'); return new Response(response.body, { status: response.status, headers }); } ``` ## When to Load References Load specific references based on the task: - **Implementing authentication?** → Load `references/authentication.md` - **CORS issues?** → Load `references/cors-security.md` - **Validating input?** → Load `references/input-validation.md` - **Managing secrets?** → Load `references/secrets-management.md` - **Rate limiting?** → Load `references/rate-limiting.md` - **Security headers?** → Load `references/security-headers.md` ## Templates | Template | Purpose | Use When | |----------|---------|----------| | `templates/auth-middleware.ts` | JWT/API key auth | Adding authentication | | `templates/cors-handler.ts` | CORS middleware | Handling cross-origin | | `templates/rate-limiter.ts` | Rate limiting | Preventing abuse | | `templates/secure-worker.ts` | Full secure setup | Starting secure project | ## Scripts | Script | Purpose | Command | |--------|---------|---------| | `scripts/security-audit.sh` | Audit security | `./security-audit.sh ` | ## Resources - Security: https://developers.cloudflare.com/workers/platform/security/ - WAF: https://developers.cloudflare.com/waf/ - Rate Limiting: https://developers.cloudflare.com/workers/runtime-apis/bindings/rate-limit/ [View on SkillFed](https://skillfed.io/secondsky/claude-skills/cloudflare-workers-security) · [View on GitHub](https://github.com/secondsky/claude-skills)