中间件的安全注意事项
中间件的安全注意事项
Express中间件是处理HTTP请求的核心组件,但不当使用可能导致严重的安全漏洞。从输入验证到依赖管理,每个环节都需要严格的安全措施。
输入验证与过滤
所有通过中间件传递的数据必须视为不可信。缺少验证的输入可能导致注入攻击或逻辑缺陷:
// 危险示例:直接使用未验证的查询参数
app.use('/search', (req, res) => {
const query = req.query.term;
db.execute(`SELECT * FROM products WHERE name LIKE '%${query}%'`);
});
// 安全做法:参数化查询
app.use('/search', (req, res) => {
const query = req.query.term;
db.execute('SELECT * FROM products WHERE name LIKE ?', [`%${query}%`]);
});
关键验证点应包括:
- 请求体参数(JSON/URL编码)
- 查询字符串参数
- 路由参数
- 上传文件名
- HTTP头部值
使用验证库如Joi或express-validator:
const { body, validationResult } = require('express-validator');
app.post('/user',
body('email').isEmail().normalizeEmail(),
body('password').isLength({ min: 8 }),
(req, res) => {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() });
}
// 处理有效数据
}
);
头部安全配置
中间件必须正确设置安全相关的HTTP头部:
const helmet = require('helmet');
app.use(helmet());
// 自定义CORS策略
app.use((req, res, next) => {
res.header('Access-Control-Allow-Origin', 'https://trusted.example.com');
res.header('X-Content-Type-Options', 'nosniff');
res.header('X-Frame-Options', 'DENY');
next();
});
特别注意:
Content-Security-Policy
防止XSSStrict-Transport-Security
强制HTTPSX-XSS-Protection
启用浏览器XSS过滤- 禁用
X-Powered-By
头
会话管理
会话中间件配置不当会导致认证绕过:
// 不安全会话配置
app.use(session({
secret: 'weak-secret',
cookie: {
httpOnly: false, // 允许JS访问
secure: false // 非HTTPS传输
}
}));
// 安全配置
app.use(session({
secret: crypto.randomBytes(32).toString('hex'),
cookie: {
httpOnly: true,
secure: true,
sameSite: 'strict',
maxAge: 24 * 60 * 60 * 1000
},
resave: false,
saveUninitialized: false
}));
必须避免:
- 使用可预测的会话ID
- 会话超时时间过长
- 在URL中传递会话标识符
文件上传处理
文件上传中间件需要特殊防护:
const multer = require('multer');
const upload = multer({
limits: {
fileSize: 1024 * 1024 * 5, // 5MB限制
files: 1
},
fileFilter: (req, file, cb) => {
const allowedTypes = ['image/jpeg', 'image/png'];
if (!allowedTypes.includes(file.mimetype)) {
return cb(new Error('Invalid file type'));
}
cb(null, true);
},
storage: multer.diskStorage({
destination: '/secure/upload/path',
filename: (req, file, cb) => {
const ext = path.extname(file.originalname);
cb(null, crypto.randomUUID() + ext); // 随机化文件名
}
})
});
app.post('/upload', upload.single('avatar'), (req, res) => {
// 处理文件
});
关键防护措施:
- 限制文件类型和大小
- 扫描上传内容(如使用clamscan)
- 存储到非Web可访问目录
- 重命名文件防止路径遍历
依赖安全
中间件依赖可能引入漏洞:
# 定期检查依赖漏洞
npm audit
npx nodejsscan --directory ./middleware
最佳实践:
- 锁定依赖版本(package-lock.json)
- 使用Snyk或Dependabot持续监控
- 移除未使用的中间件
- 审查第三方中间件源代码
错误处理
不安全的错误处理会泄露敏感信息:
// 危险示例:暴露堆栈跟踪
app.use((err, req, res, next) => {
res.status(500).send(err.stack);
});
// 安全错误处理
app.use((err, req, res, next) => {
console.error('Internal error:', err);
res.status(500).json({
error: 'An unexpected error occurred'
});
});
应确保:
- 生产环境禁用详细错误
- 记录错误到安全日志系统
- 自定义404/500错误页面
- 不暴露服务器技术栈信息
速率限制
防止暴力攻击的中间件配置:
const rateLimit = require('express-rate-limit');
const loginLimiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15分钟
max: 5, // 每个IP5次请求
message: 'Too many login attempts',
handler: (req, res) => {
res.status(429).json({
error: 'Account temporarily locked'
});
}
});
app.post('/login', loginLimiter, authController.login);
需要限制的端点:
- 用户认证相关
- 密码重置
- API关键操作
- 资源密集型请求
中间件顺序
安全相关中间件必须优先加载:
// 正确的中间件顺序
app.use(helmet());
app.use(cors());
app.use(rateLimit());
app.use(express.json());
app.use(session());
app.use(passport.initialize());
app.use(routes);
典型错误:
- 在路由之后添加安全中间件
- 会话中间件顺序不当
- 错误处理中间件未放在最后
环境变量保护
中间件配置中的敏感信息必须加密:
// 错误做法:硬编码密钥
app.use(jwt({
secret: 'my-secret-key'
}));
// 正确做法:从环境变量获取
require('dotenv').config();
app.use(jwt({
secret: process.env.JWT_SECRET,
algorithms: ['HS256']
}));
安全要求:
- 永远不提交.env文件到版本控制
- 使用加密的secret管理工具
- 为不同环境设置不同密钥
- 定期轮换密钥
请求体解析
不安全的body解析可能导致DoS攻击:
// 危险配置:未限制请求体大小
app.use(express.json());
// 安全配置
app.use(express.json({
limit: '10kb',
verify: (req, res, buf) => {
try {
JSON.parse(buf.toString());
} catch (e) {
throw new Error('Invalid JSON');
}
}
}));
特别注意:
- 限制URL编码数据大小
- 禁用宽松解析模式
- 验证内容类型头
- 处理解析错误
跨站请求伪造防护
关键操作需要CSRF保护:
const csrf = require('csurf');
const csrfProtection = csrf({
cookie: {
httpOnly: true,
sameSite: 'strict'
}
});
app.get('/form', csrfProtection, (req, res) => {
res.render('send', { csrfToken: req.csrfToken() });
});
app.post('/transfer', csrfProtection, (req, res) => {
// 处理受保护的表单
});
防护要点:
- 敏感操作使用POST请求
- 同源策略检查
- 自定义头部验证
- 状态修改操作要求二次认证
日志记录
安全日志中间件示例:
const morgan = require('morgan');
const fs = require('fs');
const path = require('path');
// 创建访问日志流
const accessLogStream = fs.createWriteStream(
path.join(__dirname, 'access.log'),
{ flags: 'a' }
);
app.use(morgan(':remote-addr - :remote-user [:date[clf]] ":method :url HTTP/:http-version" :status :res[content-length] ":referrer" ":user-agent"', {
stream: accessLogStream,
skip: (req) => req.path === '/healthcheck'
}));
// 审计日志中间件
app.use((req, res, next) => {
if (req.path.includes('/admin')) {
logAdminAction(req.user, req.path, req.method);
}
next();
});
日志安全要求:
- 不记录敏感信息(密码、token)
- 日志文件权限设置为600
- 定期轮换和归档
- 监控异常访问模式
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn
上一篇:中间件的测试策略
下一篇:中间件的日志记录与监控