Skip to content
Development
Skill

/cloudflare-workers-security

Cloudflare Workers security with authentication, CORS, rate limiting, input validation. Use for securing APIs, JWT/API keys, or encountering auth failures, CORS errors, XSS/injection vulnerabilities.

From plugin
secondsky-claude-skills
219183 skills42 agents62 commands2 MCP
Install
$ npx -y skills add secondsky/claude-skills --skill cloudflare-workers-security --agent claude-code

How it fires

How this skill gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.
  • Slash command/cloudflare-workers-security

Context preview

The summary Claude sees to decide when to auto-load this skill.

Cloudflare Workers security with authentication, CORS, rate limiting, input validation. Use for securing APIs, JWT/API keys, or encountering auth failures, CORS errors, XSS/injection vulnerabilities.

SKILL.md

cloudflare-workers-security.SKILL.md
name: cloudflare-workers-security
description: Cloudflare Workers security with authentication, CORS, rate limiting, input validation. Use for securing APIs, JWT/API keys, or encountering auth failures, CORS errors, XSS/injection vulnerabilities.
license: MIT

Cloudflare Workers Security

Comprehensive security patterns for protecting Workers and APIs.

Quick Security Checklist

// 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

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

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<string> {
  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

import { z } from 'zod';

const UserSchema = z.object({
  name: z.string().min(1).max(100),
  email: z.email(),
  age: z.number().int().min(0).max(150).optional(),
});

async function handleCreate(request: Request): Promise<Response> {
  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

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

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,
Read more
Ships withsecondsky-claude-skills

145 production-ready skills for Claude Code CLI 🔌 Platform / Harness Support These plugins ship as Claude Code marketplace plugins (.claude-plugin/ manifests) and Codex CLI plugins (.codex-plugin/ manifests).

Get the whole plugin

Other skills on secondsky-claude-skills.