
使用方式
关于
实现认证/授权、保护用户输入或防范 OWASP Top 10 漏洞时使用——包括使用 bcrypt/argon2 哈希密码、使用参数化语句净化 SQL 查询、配置 CORS/CSP 头、验证输入等自定义安全实现。
安全代码守护者
核心工作流程
- 威胁建模 — 识别攻击面和威胁
- 设计 — 规划安全控制措施
- 实现 — 编写具有纵深防御的安全代码;参见下方代码示例
- 验证 — 使用明确的检查点测试安全控制(见下方)
- 文档化 — 记录安全决策
验证检查点
每个实现步骤后,验证:
- 认证:测试暴力破解防护(锁定/限流触发)、会话固定抵抗、令牌过期,以及无效凭证错误消息(不得泄露用户是否存在)。
- 授权:验证水平和垂直权限提升路径已被阻止;使用属于不同角色/用户的令牌进行测试。
- 输入处理:确认 SQL 注入载荷(
' OR 1=1--)被拒绝;确认 XSS 载荷(<script>alert(1)</script>)被转义或拒绝。 - Headers/CORS:使用安全扫描器(如
curl -I、Mozilla Observatory)验证安全头存在且 CORS 源白名单正确。
参考指南
根据上下文加载详细指导:
| 主题 | 参考文件 | 加载时机 |
|------|----------|----------|
| OWASP | references/owasp-prevention.md | OWASP Top 10 模式 |
| 认证 | references/authentication.md | 密码哈希、JWT |
| 输入验证 | references/input-validation.md | Zod、SQL 注入 |
| XSS/CSRF | references/xss-csrf.md | XSS 防护、CSRF |
| 安全头 | references/security-headers.md | Helmet、限流 |
约束条件
必须做
- 使用 bcrypt/argon2 哈希密码(禁止 MD5/SHA-1/无盐哈希)
- 使用参数化查询(禁止字符串拼接 SQL)
- 在使用前验证和清理所有用户输入
- 在认证端点实现限流
- 设置安全头(CSP、HSTS、X-Frame-Options)
- 记录安全事件(认证失败、权限提升尝试)
- 将密钥存储在环境变量或密钥管理器中(禁止写在源代码中)
禁止做
- 以明文或可逆加密形式存储密码
- 不经验证就信任用户输入
- 在日志或错误响应中暴露敏感数据
- 使用弱或已弃用的算法(MD5、SHA-1、DES、ECB 模式)
- 在代码中硬编码密钥或凭证
代码示例
密码哈希(bcrypt)
import bcrypt from 'bcrypt';
const SALT_ROUNDS = 12; // minimum 10; 12 balances security and performance
export async function hashPassword(plaintext: string): Promise<string> {
return bcrypt.hash(plaintext, SALT_ROUNDS);
}
export async function verifyPassword(plaintext: string, hash: string): Promise<boolean> {
return bcrypt.compare(plaintext, hash);
}
参数化 SQL 查询(Node.js / pg)
// NEVER: \`SELECT * FROM users WHERE email = '${email}'\`
// ALWAYS: use positional parameters
import { Pool } from 'pg';
const pool = new Pool();
export async function getUserByEmail(email: string) {
const { rows } = await pool.query(
'SELECT id, email, role FROM users WHERE email = $1',
[email] // value passed separately — never interpolated
);
return rows[0] ?? null;
}
使用 Zod 进行输入验证
import { z } from 'zod';
const LoginSchema = z.object({
email: z.string().email().max(254),
password: z.string().min(8).max(128),
});
export function validateLoginInput(raw: unknown) {
const result = LoginSchema.safeParse(raw);
if (!result.success) {
// Return generic error — never echo raw input back
throw new Error('Invalid credentials format');
}
return result.data;
}
JWT 验证
import jwt from 'jsonwebtoken';
const JWT_SECRET = process.env.JWT_SECRET!; // never hardcode
export function verifyToken(token: string): jwt.JwtPayload {
// Throws if expired, tampered, or wrong algorithm
const payload = jwt.verify(token, JWT_SECRET, {
algorithms: ['HS256'], // explicitly allowlist algorithm
issuer: 'your-app',
audience: 'your-app',
});
if (typeof payload === 'string') throw new Error('Invalid token payload');
return payload;
}
端点安全加固 — 完整流程
import express from 'express';
import rateLimit from 'express-rate-limit';
import helmet from 'helmet';
const app = express();
app.use(helmet()); // sets CSP, HSTS, X-Frame-Options, etc.
app.use(express.json({ limit: '10kb' })); // limit payload size
const authLimiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 10, // 10 attempts per window per IP
standardHeaders: true,
legacyHeaders: false,
});
app.post('/api/login', authLimiter, async (req, res) => {
// 1. Validate input
const { email, password } = validateLoginInput(req.body);
// 2. Authenticate — parameterized query, constant-time compare
const user = await getUserByEmail(email);
if (!user || !(await verifyPassword(password, user.passwordHash))) {
// Generic message — do not reveal wh
兼容工具
Claude CodeCursor
标签
安全

