阿里云主机折上折
  • 微信号
您当前的位置:网站首页 > 中间件的安全注意事项

中间件的安全注意事项

作者:陈川 阅读数:15272人阅读 分类: Node.js

中间件的安全注意事项

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 防止XSS
  • Strict-Transport-Security 强制HTTPS
  • X-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

前端川

前端川,陈川的代码茶馆🍵,专治各种不服的Bug退散符💻,日常贩卖秃头警告级的开发心得🛠️,附赠一行代码笑十年的摸鱼宝典🐟,偶尔掉落咖啡杯里泡开的像素级浪漫☕。‌