workers-security について
Cloudflare WorkersやAPIを開発する人向けのセキュリティパターンガイドです。入力検証、認証、レート制限、レスポンスヘッダーを扱い、個別の参考資料、テンプレート、監査スクリプトも含みます。
主な機能
- 01入力検証とサニタイズ
- 02JWTとAPIキーの検証
- 03レート制限とセキュリティヘッダー
- 04CORS設定の指針
- 05個別の参考資料とコードテンプレート
- 06セキュリティ監査スクリプト
活用シーン
- 01Workerリクエストへの認証追加
- 02APIを不正利用や一般的な攻撃から保護
- 03クロスオリジンアクセスとセキュリティヘッダーの設定
- 04Workersプロジェクトのセキュリティ設定の確認
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ secondsky/ claude-skills --skill workers-securityインストール後、Claude Code で/workers-securityを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
references/authentication.mdcors-security.mdinput-validation.mdrate-limiting.mdsecrets-management.mdsecurity-headers.mdscripts/security-audit.shtemplates/auth-middleware.tscors-handler.tsrate-limiter.tssecure-worker.ts詳細
Cloudflare Workers のセキュリティ
Workers と API を保護するための包括的なセキュリティパターンです。
セキュリティのクイックチェックリスト
// 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');
重要なルール
- クライアントからの入力を決して信頼しない - すべてを検証し、サニタイズします
- シークレットを安全に使用する - Wrangler のシークレットに保存し、コードには決して記述しません
- レート制限を実装する - 悪用を防止します
- セキュリティヘッダーを設定する - よくある攻撃を防止します
- CORS を適切に使用する - 本番環境で
*を使用しないでください
よくあるセキュリティエラーのトップ 10
| 脆弱性 | 症状 | 防止策 |
|---|---|---|
| 認証の欠如 | 不正アクセス | すべてのリクエストでトークンを検証する |
| SQL インジェクション | データ侵害 | D1 ではパラメーター化クエリを使用する |
| XSS | スクリプトの挿入 | 出力をサニタイズし、CSP を設定する |
| CORS の設定ミス | リクエストのブロックまたはアクセスの開放 | 特定のオリジンを設定する |
| コード内のシークレット | 認証情報の漏えい | wrangler secret を使用する |
| レート制限の欠如 | DoS の脆弱性 | IP ごとの制限を実装する |
| 脆弱なトークン | セッションハイジャック | 署名には crypto.subtle を使用する |
| HTTPS の欠如 | データの傍受 | HTTPS へのリダイレクトを強制する |
| 安全でないヘッダー | クリックジャッキング、MIME 攻撃 | セキュリティヘッダーを設定する |
| 過剰な権限 | 被害範囲の拡大 | 最小権限の原則を適用する |
認証パターン
JWT の検証
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 キーの検証
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('');
}
入力の検証
Zod を使用する場合
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;
}
}
セキュリティヘッダー
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 の設定
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 });
}
リファレンスを読み込むタイミング
タスクに応じて、該当するリファレンスを読み込みます。
- 認証を実装する場合 →
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/auth-middleware.ts | JWT/API キー認証 | 認証を追加する場合 |
templates/cors-handler.ts | CORS ミドルウェア | クロスオリジンを処理する場合 |
templates/rate-limiter.ts | レート制限 | 悪用を防止する場合 |
templates/secure-worker.ts | セキュリティ対策を網羅した設定 | セキュアなプロジェクトを開始する場合 |
スクリプト
| スクリプト | 目的 | コマンド |
|---|---|---|
scripts/security-audit.sh | セキュリティを監査する | ./security-audit.sh <url> |
リソース
コメント
Markdown 対応