workers-security
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseCloudflare 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
核心规则
- Never trust client input - Validate and sanitize everything
- Use secure secrets - Store in Wrangler secrets, never in code
- Implement rate limiting - Protect against abuse
- Set security headers - Prevent common attacks
- Use CORS properly - Don't use in production
*
- 绝不信任客户端输入 - 验证并清理所有输入内容
- 使用安全的密钥存储 - 存储在Wrangler secrets中,绝不要写在代码里
- 实现速率限制 - 防止滥用
- 设置安全头 - 防范常见攻击
- 正确配置CORS - 生产环境不要使用
*
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 |
| 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 |
| 漏洞类型 | 症状 | 预防措施 |
|---|---|---|
| 缺少身份验证 | 未授权访问 | 对每个请求验证令牌 |
| SQL注入 | 数据泄露 | 使用D1的参数化查询 |
| XSS | 脚本注入 | 清理输出内容,设置CSP |
| CORS配置错误 | 请求被拦截或访问权限开放 | 配置特定的源地址 |
| 密钥硬编码 | 凭证泄露 | 使用 |
| 缺少速率限制 | 拒绝服务漏洞 | 实现基于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
模板
| Template | Purpose | Use When |
|---|---|---|
| JWT/API key auth | Adding authentication |
| CORS middleware | Handling cross-origin |
| Rate limiting | Preventing abuse |
| Full secure setup | Starting secure project |
| 模板 | 用途 | 使用场景 |
|---|---|---|
| JWT/API密钥认证 | 添加身份验证功能时 |
| CORS中间件 | 处理跨域请求时 |
| 速率限制 | 防止服务被滥用时 |
| 完整安全配置 | 启动安全项目时 |
Scripts
脚本
| Script | Purpose | Command |
|---|---|---|
| Audit security | |
| 脚本 | 用途 | 命令 |
|---|---|---|
| 安全审计 | |