workers-security

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Cloudflare Workers Security

Cloudflare Workers 安全防护

Comprehensive security patterns for protecting Workers and APIs.
全面的安全模式,用于保护Workers与API。

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');
typescript
// 1. 验证所有输入
const validated = schema.parse(await request.json());

// 2. 验证请求身份
const user = await verifyToken(request.headers.get('Authorization'));
if (!user) return new Response('Unauthorized', { status: 401 });

// 3. 速率限制
const limited = await rateLimiter.check(clientIP);
if (!limited.allowed) return new Response('Too Many Requests', { status: 429 });

// 4. 添加安全头
response.headers.set('X-Content-Type-Options', 'nosniff');
response.headers.set('X-Frame-Options', 'DENY');

// 5. 使用仅HTTPS的Cookie
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
  1. 绝不信任客户端输入 - 验证并清理所有输入内容
  2. 使用安全的密钥存储 - 存储在Wrangler secrets中,绝不要写在代码里
  3. 实现速率限制 - 防止滥用
  4. 设置安全头 - 防范常见攻击
  5. 正确配置CORS - 生产环境不要使用
    *

Top 10 Security Errors

十大常见安全错误

VulnerabilitySymptomPrevention
Missing authUnauthorized accessVerify tokens on every request
SQL injectionData breachUse parameterized queries with D1
XSSScript injectionSanitize output, set CSP
CORS misconfigurationBlocked requests or open accessConfigure specific origins
Secrets in codeExposed credentialsUse
wrangler secret
Missing rate limitsDoS vulnerabilityImplement per-IP limits
Weak tokensSession hijackingUse crypto.subtle for signing
Missing HTTPSData interceptionEnforce HTTPS redirects
Insecure headersClickjacking, MIME attacksSet security headers
Excessive permissionsBlast radiusPrinciple of least privilege
漏洞类型症状预防措施
缺少身份验证未授权访问对每个请求验证令牌
SQL注入数据泄露使用D1的参数化查询
XSS脚本注入清理输出内容,设置CSP
CORS配置错误请求被拦截或访问权限开放配置特定的源地址
密钥硬编码凭证泄露使用
wrangler secret
缺少速率限制拒绝服务漏洞实现基于IP的限制
弱令牌会话劫持使用crypto.subtle进行签名
缺少HTTPS数据被拦截强制HTTPS重定向
不安全的响应头点击劫持、MIME攻击设置安全响应头
权限过度影响范围扩大遵循最小权限原则

Authentication Patterns

身份验证模式

JWT Verification

JWT验证

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 };
  }
}
typescript
async function verifyJWT(token: string, secret: string): Promise<{ valid: boolean; payload?: unknown }> {
  try {
    const [headerB64, payloadB64, signatureB64] = token.split('.');

    // 验证签名
    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 };

    // 解码负载
    const payload = JSON.parse(atob(payloadB64.replace(/-/g, '+').replace(/_/g, '/')));

    // 检查过期时间
    if (payload.exp && Date.now() / 1000 > payload.exp) {
      return { valid: false };
    }

    return { valid: true, payload };
  } catch {
    return { valid: false };
  }
}

API Key Validation

API密钥验证

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<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('');
}
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 };

  // 对密钥进行哈希后查询(绝不存储明文密钥)
  const keyHash = await sha256(apiKey);

  // 在KV或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

使用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<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;
  }
}
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<Response> {
  try {
    const body = await request.json();
    const user = UserSchema.parse(body);

    // 可安全使用已验证的数据
    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 });
}
typescript
function addSecurityHeaders(response: Response): Response {
  const headers = new Headers(response.headers);

  // 防止MIME类型嗅探
  headers.set('X-Content-Type-Options', 'nosniff');

  // 防止点击劫持
  headers.set('X-Frame-Options', 'DENY');

  // XSS防护
  headers.set('X-XSS-Protection', '1; mode=block');

  // 内容安全策略
  headers.set('Content-Security-Policy', "default-src 'self'; script-src 'self'");

  // HSTS
  headers.set('Strict-Transport-Security', 'max-age=31536000; includeSubDomains');

  // 来源引用策略
  headers.set('Referrer-Policy', 'strict-origin-when-cross-origin');

  return new Response(response.body, { status: response.status, headers });
}

CORS Configuration

CORS配置

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 });
}
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; // 不添加CORS头
  }

  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
根据任务需求加载对应参考文档:
  • 实现身份验证? → 加载
    references/authentication.md
  • 遇到CORS问题? → 加载
    references/cors-security.md
  • 需要输入验证? → 加载
    references/input-validation.md
  • 密钥管理? → 加载
    references/secrets-management.md
  • 速率限制? → 加载
    references/rate-limiting.md
  • 安全响应头? → 加载
    references/security-headers.md

Templates

模板

TemplatePurposeUse When
templates/auth-middleware.ts
JWT/API key authAdding authentication
templates/cors-handler.ts
CORS middlewareHandling cross-origin
templates/rate-limiter.ts
Rate limitingPreventing abuse
templates/secure-worker.ts
Full secure setupStarting secure project
模板用途使用场景
templates/auth-middleware.ts
JWT/API密钥认证添加身份验证功能时
templates/cors-handler.ts
CORS中间件处理跨域请求时
templates/rate-limiter.ts
速率限制防止服务被滥用时
templates/secure-worker.ts
完整安全配置启动安全项目时

Scripts

脚本

ScriptPurposeCommand
scripts/security-audit.sh
Audit security
./security-audit.sh <url>
脚本用途命令
scripts/security-audit.sh
安全审计
./security-audit.sh <url>

Resources

资源