name: 安全模式 description: 实现全面的安全模式以保护应用程序免受OWASP Top 10漏洞攻击,包括XSS、CSRF、SQL注入、身份验证绕过和数据暴露。用于处理用户数据、实现身份验证和授权、验证输入、加密敏感数据、防止注入攻击、保护API端点、管理会话和令牌、实现速率限制、配置安全头或构建需要深度防御的安全关键功能。
安全模式 - 构建安全应用程序
何时使用此技能
- 实现身份验证和授权系统
- 处理敏感用户数据和个人信息
- 验证和清理所有用户输入
- 使用参数化查询防止SQL注入
- 通过输出转义保护免受XSS攻击
- 使用令牌实现CSRF保护
- 加密静态和传输中的数据
- 通过身份验证保护API端点
- 配置安全头(CSP、HSTS等)
- 实现速率限制和DDoS保护
- 管理会话、JWT和身份验证令牌
- 进行安全审计和漏洞评估
何时使用此技能
- 处理用户数据、身份验证、授权或任何安全敏感操作
- 当处理相关任务或功能时
- 在需要此专业知识的开发过程中
使用时机:处理用户数据、身份验证、授权或任何安全敏感操作
核心原则
- 深度防御 - 多层安全
- 最小权限原则 - 最小必要权限
- 安全失败 - 错误不应暴露敏感数据
- 永远不信任用户输入 - 验证一切
- 安全设计 - 不是事后考虑
OWASP Top 10保护
1. 注入预防(SQL、NoSQL、命令)
// ❌ 易受攻击 - SQL注入
app.get('/user', (req, res) => {
const query = `SELECT * FROM users WHERE id = '${req.query.id}'`;
db.query(query); // 可注入:?id=' OR '1'='1
});
// ✅ 安全 - 参数化查询
app.get('/user', (req, res) => {
const query = 'SELECT * FROM users WHERE id = ?';
db.query(query, [req.query.id]);
});
// ✅ 安全 - 带验证的ORM
app.get('/user', async (req, res) => {
const userId = parseInt(req.query.id, 10);
if (!userId || isNaN(userId)) {
return res.status(400).json({ error: '无效ID' });
}
const user = await db.users.findById(userId);
res.json(user);
});
2. 身份验证与会话管理
// ✅ 密码哈希(绝不存储明文)
import bcrypt from 'bcrypt';
async function createUser(email, password) {
const SALT_ROUNDS = 10;
const passwordHash = await bcrypt.hash(password, SALT_ROUNDS);
return await db.users.create({
email,
passwordHash // 绝不直接存储密码!
});
}
async function verifyPassword(email, password) {
const user = await db.users.findOne({ email });
if (!user) return false;
return await bcrypt.compare(password, user.passwordHash);
}
// ✅ JWT令牌身份验证
import jwt from 'jsonwebtoken';
function generateToken(userId) {
return jwt.sign(
{ userId },
process.env.JWT_SECRET, // 存储在环境变量中!
{ expiresIn: '1h' }
);
}
function verifyToken(token) {
try {
return jwt.verify(token, process.env.JWT_SECRET);
} catch (error) {
return null;
}
}
// ✅ 会话管理
import session from 'express-session';
import RedisStore from 'connect-redis';
app.use(session({
store: new RedisStore({ client: redisClient }),
secret: process.env.SESSION_SECRET,
resave: false,
saveUninitialized: false,
cookie: {
secure: true, // 仅HTTPS
httpOnly: true, // 无JavaScript访问
maxAge: 1000 * 60 * 60 * 24, // 24小时
sameSite: 'strict' // CSRF保护
}
}));
3. XSS(跨站脚本)预防
// ❌ 易受攻击 - 直接HTML插入
app.get('/profile', (req, res) => {
const html = `<h1>欢迎 ${req.query.name}</h1>`;
res.send(html); // 可注入:?name=<script>alert('xss')</script>
});
// ✅ 安全 - 模板转义(React自动转义)
function Profile({ name }) {
return <h1>欢迎 {name}</h1>; // React默认转义
}
// ✅ 安全 - 富内容显式清理
import DOMPurify from 'isomorphic-dompurify';
function RichContent({ html }) {
const sanitized = DOMPurify.sanitize(html, {
ALLOWED_TAGS: ['b', 'i', 'em', 'strong', 'p'],
ALLOWED_ATTR: []
});
return <div dangerouslySetInnerHTML={{ __html: sanitized }} />;
}
// ✅ 内容安全策略头
app.use((req, res, next) => {
res.setHeader(
'Content-Security-Policy',
"default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'"
);
next();
});
4. CSRF(跨站请求伪造)保护
// ✅ CSRF令牌
import csrf from 'csurf';
const csrfProtection = csrf({ cookie: true });
app.get('/form', csrfProtection, (req, res) => {
res.render('form', { csrfToken: req.csrfToken() });
});
app.post('/submit', csrfProtection, (req, res) => {
// 令牌自动验证
res.json({ success: true });
});
// ✅ SameSite Cookies
res.cookie('session', sessionId, {
sameSite: 'strict', // 防止CSRF
secure: true,
httpOnly: true
});
// ✅ 检查Origin/Referer头
app.use((req, res, next) => {
const origin = req.get('origin') || req.get('referer');
if (origin && !origin.startsWith('https://yourdomain.com')) {
return res.status(403).json({ error: '无效来源' });
}
next();
});
5. 访问控制与授权
// ✅ 基于角色的访问控制(RBAC)
const ROLES = {
ADMIN: 'admin',
USER: 'user',
GUEST: 'guest'
};
function requireRole(role) {
return (req, res, next) => {
if (!req.user) {
return res.status(401).json({ error: '未认证' });
}
if (req.user.role !== role) {
return res.status(403).json({ error: '权限不足' });
}
next();
};
}
app.delete('/users/:id', requireRole(ROLES.ADMIN), async (req, res) => {
await db.users.delete(req.params.id);
res.json({ success: true });
});
// ✅ 基于资源的访问控制
async function canEditPost(userId, postId) {
const post = await db.posts.findById(postId);
if (!post) return false;
// 用户可以编辑自己的帖子或如果是管理员
const user = await db.users.findById(userId);
return post.authorId === userId || user.role === 'admin';
}
app.put('/posts/:id', async (req, res) => {
if (!await canEditPost(req.user.id, req.params.id)) {
return res.status(403).json({ error: '无法编辑此帖子' });
}
const updated = await db.posts.update(req.params.id, req.body);
res.json(updated);
});
6. 速率限制与DDoS保护
// ✅ 速率限制
import rateLimit from 'express-rate-limit';
const limiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15分钟
max: 100, // 每个IP限制100个请求每窗口
message: '请求过多,请稍后再试'
});
app.use('/api/', limiter);
// ✅ 认证端点的更严格限制
const authLimiter = rateLimit({
windowMs: 15 * 60 * 1000,
max: 5, // 每15分钟仅5次登录尝试
skipSuccessfulRequests: true
});
app.post('/login', authLimiter, async (req, res) => {
// 登录逻辑
});
// ✅ 基于Redis的速率限制(分布式)
import RedisStore from 'rate-limit-redis';
const limiter = rateLimit({
store: new RedisStore({
client: redisClient
}),
windowMs: 15 * 60 * 1000,
max: 100
});
7. 敏感数据暴露预防
// ❌ 易受攻击 - 暴露敏感数据
app.get('/user/:id', async (req, res) => {
const user = await db.users.findById(req.params.id);
res.json(user); // 包括密码哈希、内部ID等!
});
// ✅ 安全 - 显式字段选择
app.get('/user/:id', async (req, res) => {
const user = await db.users.findById(req.params.id);
res.json({
id: user.id,
name: user.name,
email: user.email
// 不包括passwordHash
});
});
// ✅ 安全 - 使用DTOs/序列化器
class UserDTO {
static fromUser(user) {
return {
id: user.id,
name: user.name,
email: user.email,
createdAt: user.createdAt
};
}
}
app.get('/user/:id', async (req, res) => {
const user = await db.users.findById(req.params.id);
res.json(UserDTO.fromUser(user));
});
// ✅ 加密静态敏感数据
import crypto from 'crypto';
function encrypt(text) {
const key = Buffer.from(process.env.ENCRYPTION_KEY, 'hex');
const iv = crypto.randomBytes(16);
const cipher = crypto.createCipheriv('aes-256-cbc', key, iv);
let encrypted = cipher.update(text, 'utf8', 'hex');
encrypted += cipher.final('hex');
return iv.toString('hex') + ':' + encrypted;
}
function decrypt(encrypted) {
const key = Buffer.from(process.env.ENCRYPTION_KEY, 'hex');
const parts = encrypted.split(':');
const iv = Buffer.from(parts[0], 'hex');
const encryptedText = parts[1];
const decipher = crypto.createDecipheriv('aes-256-cbc', key, iv);
let decrypted = decipher.update(encryptedText, 'hex', 'utf8');
decrypted += decipher.final('utf8');
return decrypted;
}
8. 输入验证
// ✅ 使用Zod的模式验证
import { z } from 'zod';
const CreateUserSchema = z.object({
email: z.string().email(),
password: z.string().min(8).max(100),
age: z.number().int().min(0).max(150),
website: z.string().url().optional()
});
app.post('/users', async (req, res) => {
try {
const validated = CreateUserSchema.parse(req.body);
const user = await createUser(validated);
res.json(user);
} catch (error) {
res.status(400).json({ error: error.errors });
}
});
// ✅ 清理文件上传
import multer from 'multer';
import path from 'path';
const upload = multer({
dest: 'uploads/',
limits: {
fileSize: 5 * 1024 * 1024 // 最大5MB
},
fileFilter: (req, file, cb) => {
const allowedTypes = ['image/jpeg', 'image/png', 'image/gif'];
if (!allowedTypes.includes(file.mimetype)) {
return cb(new Error('无效文件类型'));
}
cb(null, true);
}
});
app.post('/upload', upload.single('avatar'), (req, res) => {
res.json({ filename: req.file.filename });
});
9. 日志记录与监控
// ✅ 安全事件日志记录
import winston from 'winston';
const securityLogger = winston.createLogger({
level: 'info',
format: winston.format.json(),
transports: [
new winston.transports.File({ filename: 'security.log' })
]
});
// 记录失败的身份验证尝试
app.post('/login', async (req, res) => {
const user = await verifyCredentials(req.body.email, req.body.password);
if (!user) {
securityLogger.warn('登录尝试失败', {
email: req.body.email,
ip: req.ip,
userAgent: req.get('user-agent'),
timestamp: new Date().toISOString()
});
return res.status(401).json({ error: '无效凭证' });
}
res.json({ token: generateToken(user.id) });
});
// 记录权限提升尝试
app.put('/users/:id/role', requireAdmin, async (req, res) => {
securityLogger.info('角色变更', {
adminId: req.user.id,
targetUserId: req.params.id,
newRole: req.body.role,
timestamp: new Date().toISOString()
});
await db.users.updateRole(req.params.id, req.body.role);
res.json({ success: true });
});
10. 安全头
// ✅ 基本安全头
import helmet from 'helmet';
app.use(helmet()); // 自动设置多个头
// 或手动:
app.use((req, res, next) => {
// 防止点击劫持
res.setHeader('X-Frame-Options', 'DENY');
// 防止MIME嗅探
res.setHeader('X-Content-Type-Options', 'nosniff');
// 启用XSS过滤器
res.setHeader('X-XSS-Protection', '1; mode=block');
// 严格传输安全(仅HTTPS)
res.setHeader('Strict-Transport-Security', 'max-age=31536000; includeSubDomains');
// 引用策略
res.setHeader('Referrer-Policy', 'strict-origin-when-cross-origin');
// 权限策略
res.setHeader('Permissions-Policy', 'geolocation=(), microphone=(), camera=()');
next();
});
环境与秘密管理
// ❌ 绝不要这样做
const apiKey = 'sk_live_abc123'; // 硬编码秘密
const dbPassword = 'password123';
// ✅ 使用环境变量
import dotenv from 'dotenv';
dotenv.config();
const apiKey = process.env.API_KEY;
const dbPassword = process.env.DB_PASSWORD;
// ✅ 启动时验证必需秘密
const requiredEnvVars = [
'DATABASE_URL',
'JWT_SECRET',
'API_KEY'
];
for (const envVar of requiredEnvVars) {
if (!process.env[envVar]) {
console.error(`缺少必需环境变量:${envVar}`);
process.exit(1);
}
}
// ✅ 使用秘密管理服务(生产环境)
import { SecretsManager } from '@aws-sdk/client-secrets-manager';
async function getSecret(secretName) {
const client = new SecretsManager({ region: 'us-east-1' });
const response = await client.getSecretValue({ SecretId: secretName });
return JSON.parse(response.SecretString);
}
安全清单
身份验证与会话:
□ 密码使用bcrypt/argon2哈希
□ JWT秘密安全存储
□ 令牌过期已实现
□ 会话cookie:httpOnly、secure、sameSite
□ 敏感操作提供MFA
输入验证:
□ 所有输入使用模式验证
□ SQL注入已防止(参数化查询)
□ XSS已防止(输出转义)
□ 文件上传受限并验证
□ 重定向前验证URL
授权:
□ 受保护路由需要身份验证
□ 基于角色的访问控制已实现
□ 资源所有权已验证
□ 最小权限原则已强制执行
数据保护:
□ 处处强制执行HTTPS
□ 静态敏感数据已加密
□ 秘密在环境变量中(不在代码中)
□ 日志/错误中无敏感数据
□ PII处理符合法规
基础设施:
□ 公共端点速率限制
□ 安全头已配置
□ CORS正确配置
□ 依赖漏洞已扫描
□ 安全监控和警报
审计与合规:
□ 安全事件已记录
□ 访问日志已保留
□ 定期安全审计已安排
□ 事件响应计划已记录
资源
记住:安全不是可选的。必须从第一天起就内置到每个层面。