security-hardening
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseSecurity Hardening
安全加固
Input Validation
输入校验
Validate all input at the boundary. Never trust client-side validation alone.
typescript
import { z } from 'zod';
const CreateUserSchema = z.object({
email: z.string().email().max(255),
name: z.string().min(1).max(100).regex(/^[a-zA-Z\s'-]+$/),
age: z.number().int().min(13).max(150),
});
function createUser(req: Request) {
const result = CreateUserSchema.safeParse(req.body);
if (!result.success) {
return { status: 400, errors: result.error.flatten().fieldErrors };
}
// result.data is typed and validated
}Rules:
- Validate type, length, format, and range on every input
- Use allowlists over denylists (accept known good, reject everything else)
- Validate file uploads: check MIME type, file extension, and magic bytes
- Limit request body size at the server/proxy level (e.g., 1MB max)
在边界层校验所有输入,永远不要只依赖客户端校验。
typescript
import { z } from 'zod';
const CreateUserSchema = z.object({
email: z.string().email().max(255),
name: z.string().min(1).max(100).regex(/^[a-zA-Z\s'-]+$/),
age: z.number().int().min(13).max(150),
});
function createUser(req: Request) {
const result = CreateUserSchema.safeParse(req.body);
if (!result.success) {
return { status: 400, errors: result.error.flatten().fieldErrors };
}
// result.data is typed and validated
}规则:
- 对所有输入的类型、长度、格式和范围进行校验
- 优先使用允许列表而非拒绝列表(接受已知合法内容,拒绝其余所有内容)
- 校验文件上传:检查MIME类型、文件扩展名和魔数
- 在服务端/代理层限制请求体大小(例如最大1MB)
Output Encoding
输出编码
typescript
// Prevent XSS: encode output based on context
// HTML context: use framework auto-escaping (React does this by default)
// Never use dangerouslySetInnerHTML with user input
// URL context: encode parameters
const safeUrl = `/search?q=${encodeURIComponent(userInput)}`;
// JSON context: use JSON.stringify (handles escaping)
const safeJson = JSON.stringify({ query: userInput });Never construct HTML strings with user input. Use templating engines with auto-escaping enabled.
typescript
// Prevent XSS: encode output based on context
// HTML context: use framework auto-escaping (React does this by default)
// Never use dangerouslySetInnerHTML with user input
// URL context: encode parameters
const safeUrl = `/search?q=${encodeURIComponent(userInput)}`;
// JSON context: use JSON.stringify (handles escaping)
const safeJson = JSON.stringify({ query: userInput });永远不要使用用户输入拼接HTML字符串,使用开启了自动转义的模板引擎。
SQL Injection Prevention
SQL注入防护
python
undefinedpython
undefinedNEVER do this
NEVER do this
cursor.execute(f"SELECT * FROM users WHERE id = {user_id}")
cursor.execute(f"SELECT * FROM users WHERE id = {user_id}")
Always use parameterized queries
Always use parameterized queries
cursor.execute("SELECT * FROM users WHERE id = %s", (user_id,))
```typescript
// NEVER do this
db.query(`SELECT * FROM users WHERE email = '${email}'`);
// Always use parameterized queries
db.query("SELECT * FROM users WHERE email = $1", [email]);Use an ORM or query builder. If writing raw SQL, always parameterize.
cursor.execute("SELECT * FROM users WHERE id = %s", (user_id,))
```typescript
// NEVER do this
db.query(`SELECT * FROM users WHERE email = '${email}'`);
// Always use parameterized queries
db.query("SELECT * FROM users WHERE email = $1", [email]);使用ORM或者查询构建器。如果需要编写原生SQL,必须使用参数化查询。
CSRF Protection
CSRF防护
typescript
// Server: generate and validate CSRF tokens
import { randomBytes } from 'crypto';
function generateCsrfToken(): string {
return randomBytes(32).toString('hex');
}
// Middleware: validate on state-changing requests
function csrfMiddleware(req, res, next) {
if (['POST', 'PUT', 'PATCH', 'DELETE'].includes(req.method)) {
const token = req.headers['x-csrf-token'] || req.body._csrf;
if (!timingSafeEqual(token, req.session.csrfToken)) {
return res.status(403).json({ error: 'Invalid CSRF token' });
}
}
next();
}For APIs with token-based auth (Bearer tokens), CSRF is not needed since the token is not auto-sent by browsers.
typescript
// Server: generate and validate CSRF tokens
import { randomBytes } from 'crypto';
function generateCsrfToken(): string {
return randomBytes(32).toString('hex');
}
// Middleware: validate on state-changing requests
function csrfMiddleware(req, res, next) {
if (['POST', 'PUT', 'PATCH', 'DELETE'].includes(req.method)) {
const token = req.headers['x-csrf-token'] || req.body._csrf;
if (!timingSafeEqual(token, req.session.csrfToken)) {
return res.status(403).json({ error: 'Invalid CSRF token' });
}
}
next();
}对于使用基于令牌的身份验证(Bearer tokens)的API,不需要CSRF防护,因为浏览器不会自动发送这类令牌。
Content Security Policy
内容安全策略
Content-Security-Policy:
default-src 'self';
script-src 'self' 'nonce-{random}';
style-src 'self' 'unsafe-inline';
img-src 'self' data: https:;
font-src 'self';
connect-src 'self' https://api.example.com;
frame-ancestors 'none';
base-uri 'self';
form-action 'self';Start strict, relax as needed. Use for inline scripts instead of . Report violations with directive. Test with first.
nonceunsafe-inlinereport-uriContent-Security-Policy-Report-OnlyContent-Security-Policy:
default-src 'self';
script-src 'self' 'nonce-{random}';
style-src 'self' 'unsafe-inline';
img-src 'self' data: https:;
font-src 'self';
connect-src 'self' https://api.example.com;
frame-ancestors 'none';
base-uri 'self';
form-action 'self';初始配置从严格模式开始,再按需放宽限制。对 inline 脚本使用而非。使用指令上报违规情况。先使用模式进行测试。
nonceunsafe-inlinereport-uriContent-Security-Policy-Report-OnlySecurity Headers
安全请求头
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
Referrer-Policy: strict-origin-when-cross-origin
Permissions-Policy: camera=(), microphone=(), geolocation=()Set these on every response. Use (Node.js) or equivalent middleware.
helmetStrict-Transport-Security: max-age=31536000; includeSubDomains; preload
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
Referrer-Policy: strict-origin-when-cross-origin
Permissions-Policy: camera=(), microphone=(), geolocation=()对所有响应设置上述请求头,使用(Node.js)或对等中间件实现配置。
helmetRate Limiting
限流
typescript
// Per-user, per-endpoint rate limiting
const rateLimits = {
'POST /auth/login': { window: '15m', max: 5 },
'POST /auth/register': { window: '1h', max: 3 },
'POST /api/*': { window: '1m', max: 60 },
'GET /api/*': { window: '1m', max: 120 },
};Use sliding window algorithm. Store counters in Redis. Return with header. Apply stricter limits to authentication endpoints.
429Retry-Aftertypescript
// Per-user, per-endpoint rate limiting
const rateLimits = {
'POST /auth/login': { window: '15m', max: 5 },
'POST /auth/register': { window: '1h', max: 3 },
'POST /api/*': { window: '1m', max: 60 },
'GET /api/*': { window: '1m', max: 120 },
};使用滑动窗口算法,在Redis中存储计数器。返回状态码和请求头。对身份验证相关接口应用更严格的限流规则。
429Retry-AfterJWT Best Practices
JWT最佳实践
- Use short expiry (15 minutes) for access tokens
- Use refresh tokens (7-30 days) stored in httpOnly cookies
- Sign with RS256 (asymmetric) for microservices, HS256 (symmetric) for monoliths
- Never store sensitive data in JWT payload (it is base64 encoded, not encrypted)
- Validate ,
iss,aud, andexpclaims on every requestnbf - Implement token revocation via a denylist or short expiry + rotation
typescript
// Verify JWT with all checks
const payload = jwt.verify(token, publicKey, {
algorithms: ['RS256'],
issuer: 'auth.example.com',
audience: 'api.example.com',
clockTolerance: 30,
});- 访问令牌使用短有效期(15分钟)
- 刷新令牌有效期设置为7-30天,存储在httpOnly cookie中
- 微服务场景使用RS256(非对称加密)签名,单体应用使用HS256(对称加密)签名
- 永远不要在JWT payload中存储敏感数据(payload仅做base64编码,未加密)
- 每次请求都校验、
iss、aud和exp声明nbf - 通过拒绝列表或短有效期+令牌轮换实现令牌撤销
typescript
// Verify JWT with all checks
const payload = jwt.verify(token, publicKey, {
algorithms: ['RS256'],
issuer: 'auth.example.com',
audience: 'api.example.com',
clockTolerance: 30,
});Secrets Management
密钥管理
- Never commit secrets to version control (use for
.gitignore).env - Use environment variables for runtime secrets
- Use a secrets manager in production (AWS Secrets Manager, HashiCorp Vault, Doppler)
- Rotate secrets regularly (90-day maximum for API keys)
- Use different secrets per environment (dev/staging/prod)
- Scan for leaked secrets in CI: ,
trufflehog,gitleaksgit-secrets
bash
undefined- 永远不要将密钥提交到版本控制系统(使用忽略
.gitignore文件).env - 运行时密钥使用环境变量存储
- 生产环境使用密钥管理服务(AWS Secrets Manager、HashiCorp Vault、Doppler)
- 定期轮换密钥(API密钥最长有效期为90天)
- 不同环境使用不同密钥(开发/预发布/生产)
- 在CI中扫描泄露的密钥:、
trufflehog、gitleaksgit-secrets
bash
undefinedCheck for secrets in git history
Check for secrets in git history
gitleaks detect --source . --verbose
gitleaks detect --source . --verbose
Pre-commit hook to prevent secret commits
Pre-commit hook to prevent secret commits
gitleaks protect --staged
undefinedgitleaks protect --staged
undefinedDependency Auditing
依赖审计
bash
undefinedbash
undefinedNode.js
Node.js
npm audit --production
npx better-npm-audit audit --level=high
npm audit --production
npx better-npm-audit audit --level=high
Python
Python
pip-audit
safety check
pip-audit
safety check
Go
Go
govulncheck ./...
Run dependency audits in CI on every PR. Block merges on critical/high vulnerabilities. Pin dependency versions. Update dependencies weekly with automated PRs (Dependabot, Renovate).govulncheck ./...
每次PR提交时在CI中运行依赖审计,存在严重/高危漏洞时阻止合并。固定依赖版本,通过自动PR(Dependabot、Renovate)每周更新依赖。Checklist Before Deploy
部署前检查清单
- All inputs validated with schema validation
- SQL queries parameterized
- Security headers configured
- HTTPS enforced with HSTS
- Secrets externalized, not in code
- Dependencies audited, no critical vulnerabilities
- Rate limiting on all public endpoints
- Authentication tokens expire and rotate
- Error messages do not leak internal details
- Logging captures security events without sensitive data
- 所有输入都通过了schema校验
- SQL查询都使用了参数化
- 安全请求头已配置
- 通过HSTS强制使用HTTPS
- 密钥已外置,不存在于代码中
- 依赖已完成审计,无严重漏洞
- 所有公开接口都配置了限流
- 身份验证令牌支持过期和轮换
- 错误信息不会泄露内部细节
- 日志记录安全事件且不含敏感数据