跳到主要内容

Web 安全速查表

本文档提供 Web 安全防御的各种配置、代码片段和最佳实践清单。建议开发者在编码和部署时对照检查。

1. SQL 注入防护

参数化查询速查

语言/框架安全写法说明
Node.js (mysql2)db.query('SELECT * FROM users WHERE id = ?', [id])使用 ? 占位符
Node.js (pg)pool.query('SELECT * FROM users WHERE id = $1', [id])使用 $1, $2 占位符
Python (sqlite3)cursor.execute('SELECT * FROM users WHERE id = ?', (id,))使用 ? 占位符
Python (psycopg2)cursor.execute('SELECT * FROM users WHERE id = %s', (id,))使用 %s 占位符
Java (JDBC)PreparedStatement stmt = conn.prepareStatement("SELECT * FROM users WHERE id = ?")使用预编译语句
Java (MyBatis)SELECT * FROM users WHERE id = #{id}使用 #{}(不是 ${}
Go (database/sql)db.Query("SELECT * FROM users WHERE id = ?", id)使用 ? 占位符
PHP (PDO)$stmt = $pdo->prepare('SELECT * FROM users WHERE id = ?'); $stmt->execute([$id])使用预编译语句

危险模式检查

# 搜索代码中的危险模式
grep -r "SELECT.*+.*" . # 字符串拼接
grep -r '\${' . --include="*.xml" # MyBatis 字符串替换
grep -r "queryRawUnsafe" . # Prisma 不安全查询
grep -r "execute(" . # 原生 SQL 执行

2. XSS 防护

输出编码规则

上下文编码方法示例
HTML 内容HTML 实体编码<&lt; >&gt; &&amp;
HTML 属性属性编码 + 引号包围"&quot; '&#x27;
JavaScriptUnicode 编码"\u0022
URL百分号编码 %20
CSS反斜杠十六进制"\000022

安全的 DOM 操作

// ✅ 安全:自动编码
element.textContent = userInput;
element.setAttribute('data-value', userInput);
element.className = userInput;

// ❌ 危险:不编码
element.innerHTML = userInput;
document.write(userInput);

框架安全边界

框架安全用法危险用法
React<div>{input}</div>dangerouslySetInnerHTML={{__html: input}}
Vue{{ input }}v-html="input"
Angular{{ input }}[innerHTML]="input"

富文本净化

import DOMPurify from 'dompurify';

// 基本净化
const clean = DOMPurify.sanitize(dirtyHTML);

// 允许特定标签
const clean = DOMPurify.sanitize(dirtyHTML, {
ALLOWED_TAGS: ['p', 'b', 'i', 'strong', 'em', 'a', 'ul', 'ol', 'li'],
ALLOWED_ATTR: ['href', 'title', 'target']
});

3. CSRF 防护

Set-Cookie: session=xxx; 
HttpOnly; /* 禁止 JS 读取,防 XSS */
Secure; /* 仅 HTTPS 传输 */
SameSite=Lax; /* 防跨站请求 */
Path=/;
Max-Age=3600

SameSite 属性选择

行为适用场景
Strict完全禁止跨站发送敏感操作、支付
Lax允许顶级导航 GET 携带大多数 Cookie(默认推荐)
None允许跨站(需 Secure)OAuth 回调、第三方集成

CSRF Token 实现

// 服务端:生成 Token
const crypto = require('crypto');
const csrfToken = crypto.randomBytes(32).toString('hex');
req.session.csrfToken = csrfToken;

// 服务端:验证 Token
if (req.body._csrf !== req.session.csrfToken) {
return res.status(403).send('CSRF 验证失败');
}

// 前端:表单中携带
<input type="hidden" name="_csrf" value="<%= csrfToken %>">

// 前端:AJAX Header 携带
headers: { 'X-CSRF-Token': csrfToken }

4. HTTP 安全头配置

完整安全头配置

# 强制 HTTPS
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload

# 防止 MIME 嗅探
X-Content-Type-Options: nosniff

# 防止点击劫持
X-Frame-Options: SAMEORIGIN

# XSS 保护(现代浏览器已内置,但保留兼容)
X-XSS-Protection: 1; mode=block

# 内容安全策略
Content-Security-Policy: default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'

# 引用策略
Referrer-Policy: strict-origin-when-cross-origin

# 权限策略
Permissions-Policy: geolocation=(), microphone=(), camera=()

Nginx 配置

server {
# 强制 HTTPS
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;

# 安全头
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;

# CSP(根据应用调整)
add_header Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; font-src 'self';" always;

# 移除敏感信息
server_tokens off;
}

Apache 配置

<IfModule mod_headers.c>
Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
Header always set X-Content-Type-Options "nosniff"
Header always set X-Frame-Options "SAMEORIGIN"
Header always set X-XSS-Protection "1; mode=block"
Header always set Referrer-Policy "strict-origin-when-cross-origin"
Header always set Content-Security-Policy "default-src 'self'"
</IfModule>

# 移除服务器签名
ServerSignature Off
ServerTokens Prod

Express.js 配置

const helmet = require('helmet');

app.use(helmet()); // 应用所有安全头

// 或单独配置
app.use(helmet.contentSecurityPolicy({
directives: {
defaultSrc: ["'self'"],
scriptSrc: ["'self'"],
styleSrc: ["'self'", "'unsafe-inline'"],
imgSrc: ["'self'", 'data:'],
}
}));

5. 密码安全

密码哈希算法选择

算法推荐度说明
Argon2⭐⭐⭐⭐⭐最推荐,抗 GPU 破解
bcrypt⭐⭐⭐⭐广泛支持,成熟稳定
scrypt⭐⭐⭐⭐内存困难,抗 ASIC
PBKDF2⭐⭐⭐标准算法,兼容性好
SHA256/MD5不推荐,易破解

密码哈希实现

// Node.js - bcrypt
const bcrypt = require('bcrypt');
const hashedPassword = await bcrypt.hash(password, 12); // cost factor 12
const isValid = await bcrypt.compare(password, hashedPassword);

// Python - bcrypt
import bcrypt
hashed = bcrypt.hashpw(password.encode(), bcrypt.gensalt(rounds=12))
is_valid = bcrypt.checkpw(password.encode(), hashed)

// Java - BCrypt
import org.mindrot.jbcrypt.BCrypt;
String hashed = BCrypt.hashpw(password, BCrypt.gensalt(12));
boolean isValid = BCrypt.checkpw(password, hashed);

// Go - bcrypt
import "golang.org/x/crypto/bcrypt"
hashed, _ := bcrypt.GenerateFromPassword([]byte(password), 12)
err := bcrypt.CompareHashAndPassword(hashed, []byte(password))

密码强度要求

function validatePassword(password) {
const rules = {
minLength: password.length >= 12,
hasUpper: /[A-Z]/.test(password),
hasLower: /[a-z]/.test(password),
hasNumber: /[0-9]/.test(password),
hasSpecial: /[!@#$%^&*()_+\-=\[\]{};':"\\|,.<>\/?]/.test(password),
notCommon: !commonPasswords.includes(password.toLowerCase())
};

return Object.values(rules).every(Boolean);
}

6. 加密与数据保护

加密算法选择速查

场景推荐算法密钥长度说明
密码存储Argon2id-首选,抗 GPU/ASIC
密码存储(备选)bcrypt-成熟稳定
对称加密AES-256-GCM256 位认证加密
移动端加密ChaCha20-Poly1305256 位无需硬件加速
非对称加密ECDSA/Ed25519256 位比 RSA 更高效
密钥交换ECDH(Curve25519)256 位TLS 1.3 默认
数字签名Ed25519256 位高效安全
哈希SHA-256/SHA-3-通用安全哈希

Argon2id 推荐配置

内存 (m)迭代次数 (t)并行度 (p)适用场景
46 MiB11内存充足
19 MiB21平衡配置(推荐)
12 MiB31内存受限

AES-GCM 快速实现

// Node.js - AES-GCM 加密
const crypto = require('crypto');

function encrypt(key, plaintext) {
const iv = crypto.randomBytes(12); // 12 字节 IV
const cipher = crypto.createCipheriv('aes-256-gcm', key, iv);
let encrypted = Buffer.concat([cipher.update(plaintext, 'utf8'), cipher.final()]);
const authTag = cipher.getAuthTag();
return Buffer.concat([iv, authTag, encrypted]).toString('base64');
}

function decrypt(key, encryptedData) {
const data = Buffer.from(encryptedData, 'base64');
const iv = data.subarray(0, 12);
const authTag = data.subarray(12, 28);
const encrypted = data.subarray(28);
const decipher = crypto.createDecipheriv('aes-256-gcm', key, iv);
decipher.setAuthTag(authTag);
let decrypted = Buffer.concat([decipher.update(encrypted), decipher.final()]);
return decrypted.toString('utf8');
}

安全随机数生成

语言不安全安全
JavaScriptMath.random()crypto.randomBytes()
Pythonrandom.random()secrets.token_bytes()
Javajava.util.Randomjava.security.SecureRandom
Gomath/randcrypto/rand
PHPrand()random_bytes()

TLS 配置速查

# Nginx - 现代 TLS 配置
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384;
ssl_prefer_server_ciphers off;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 1d;
ssl_session_tickets off;
ssl_stapling on;
ssl_stapling_verify on;

# HSTS
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;

密钥管理原则

原则说明
不硬编码密钥不能写入代码或配置文件
分离存储密钥与加密数据分开存放
最小权限只授予必要的密钥访问权限
定期轮换按策略定期更换密钥
使用 KMS优先使用云密钥管理服务

数据脱敏规则

数据类型脱敏规则示例
手机号保留前 3 后 4138****5678
身份证保留前 3 后 4310***********1234
银行卡保留前 4 后 46222 **** **** 7890
邮箱保留首尾字符t***[email protected]
姓名保留首字张**

8. 身份认证

JWT 安全配置

// 安全的 JWT 配置
const token = jwt.sign(
{ userId: user.id, role: user.role }, // 只放必要信息
process.env.JWT_SECRET, // 密钥从环境变量读取
{
algorithm: 'RS256', // 使用非对称算法
expiresIn: '15m', // 短有效期
issuer: 'your-app',
audience: 'your-app-users'
}
);

// 验证时严格检查
jwt.verify(token, publicKey, {
algorithms: ['RS256'], // 明确指定算法
issuer: 'your-app',
audience: 'your-app-users'
});

会话安全配置

// Express.js 会话配置
const session = require('express-session');

app.use(session({
secret: process.env.SESSION_SECRET,
resave: false,
saveUninitialized: false,
cookie: {
httpOnly: true, // 防 XSS
secure: true, // 仅 HTTPS
sameSite: 'strict', // 防 CSRF
maxAge: 3600000 // 1 小时
}
}));

登录安全措施

// 防暴力破解
const rateLimit = require('express-rate-limit');

const loginLimiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 分钟
max: 5, // 最多 5 次尝试
message: '尝试次数过多,请稍后再试'
});

app.post('/login', loginLimiter, (req, res) => {
// 登录逻辑
});

// 账户锁定
const failedAttempts = new Map();

function checkLockout(username) {
const attempts = failedAttempts.get(username) || 0;
if (attempts >= 5) {
const lockoutTime = lockouts.get(username);
if (lockoutTime && Date.now() < lockoutTime) {
return { locked: true, remainingTime: lockoutTime - Date.now() };
}
}
return { locked: false };
}

9. 依赖安全

定期扫描

# Node.js
npm audit
npm audit fix

# 使用 Snyk
npx snyk test
npx snyk monitor

# Python
pip-audit
safety check

# Go
go list -m -u all
trivy fs .

# Java (Maven)
mvn dependency-check:check

依赖固定

// package-lock.json - 始终提交
// 使用 npm ci 而非 npm install

// Pipfile.lock - 始终提交
// pipenv sync

// go.sum - 始终提交

CI/CD 集成

# GitHub Actions
name: Security Scan
on: [push, pull_request]

jobs:
security:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3

- name: Run npm audit
run: npm audit --audit-level=high

- name: Run Snyk
uses: snyk/actions/node@master
env:
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}

- name: Run Trivy
uses: aquasecurity/trivy-action@master
with:
scan-type: 'fs'
severity: 'HIGH,CRITICAL'

10. 安全日志

应记录的安全事件

事件类型示例
认证事件登录成功/失败、登出、密码重置
授权事件权限检查失败、越权尝试
敏感操作数据导出、配置变更、删除操作
异常行为速率限制触发、异常 IP 访问
系统事件服务启动/停止、配置加载

日志格式

// 安全日志结构
const securityLog = {
timestamp: '2024-01-15T10:30:00Z',
eventType: 'LOGIN_FAILURE',
userId: 'user123',
ip: '192.168.1.1',
userAgent: 'Mozilla/5.0...',
details: {
reason: 'INVALID_PASSWORD',
attempts: 3
},
requestId: 'req-abc-123'
};

日志安全原则

  • ❌ 不记录密码、令牌、信用卡号等敏感数据
  • ❌ 不记录完整的请求体或响应体
  • ✅ 记录用户标识、IP、时间、操作类型
  • ✅ 使用结构化日志格式(JSON)
  • ✅ 集中存储,限制访问权限

11. 安全检查清单

上线前检查

认证授权

  • 密码使用安全哈希存储(bcrypt/Argon2)
  • 实施密码强度要求
  • 敏感操作需要重新认证
  • 会话有合理的超时时间
  • 登出后彻底销毁会话

输入验证

  • 所有外部输入都经过验证
  • 使用白名单验证而非黑名单
  • 服务端验证(不依赖前端)
  • 文件上传有类型和大小限制

输出编码

  • 根据上下文正确编码输出
  • 使用框架的安全特性
  • 富文本使用 DOMPurify 净化

访问控制

  • 实施最小权限原则
  • 每个请求都验证权限
  • 不暴露内部实现细节

加密

  • 强制 HTTPS
  • Cookie 设置 Secure 标志
  • 敏感数据加密存储
  • 密钥安全存储

配置

  • 移除默认凭据
  • 禁用不必要的功能
  • 设置安全 HTTP 头
  • 错误信息不泄露敏感数据

依赖

  • 依赖已通过安全扫描
  • 使用最新稳定版本
  • 锁定依赖版本

12. 常用工具

安全扫描

工具类型说明
OWASP ZAPDAST动态应用安全测试
Burp SuiteDAST渗透测试工具
SonarQubeSAST静态代码分析
SnykSCA依赖漏洞扫描
Trivy容器安全容器镜像扫描
SQLMap专用SQL 注入检测

在线资源


参考文档