Express 安全指南

Express.js 应用安全加固要点:HTTP 头、限流、输入验证、CSRF 和注入防御。

1. Helmet — 安全 HTTP 头

const helmet = require('helmet');

app.use(helmet());

// 自定义 CSP
app.use(helmet.contentSecurityPolicy({
  directives: {
    defaultSrc: ["'self'"],
    scriptSrc: ["'self'", 'cdn.jsdelivr.net'],
    imgSrc: ["'self'", 'data:', 'https:'],
    frameSrc: ["'none'"],
    objectSrc: ["'none'"],
  },
}));

app.use(helmet.hsts({ maxAge: 63072000, includeSubDomains: true }));

2. 限流

const rateLimit = require('express-rate-limit');

const apiLimiter = rateLimit({
  windowMs: 15 * 60 * 1000,
  max: 100,
  standardHeaders: true,
  message: { error: '请求过多,请稍后重试。' },
});

const authLimiter = rateLimit({
  windowMs: 60 * 60 * 1000,
  max: 10,
  skipSuccessfulRequests: true,
});

app.use('/api/', apiLimiter);
app.post('/auth/login', authLimiter, loginHandler);

3. 输入验证 (Zod)

const { z } = require('zod');

const createUserSchema = z.object({
  email: z.string().email().max(255),
  password: z.string().min(8).max(128),
  name: z.string().min(1).max(100).trim(),
});

function validate(schema) {
  return (req, res, next) => {
    const result = schema.safeParse(req.body);
    if (!result.success) {
      return res.status(422).json({
        error: '验证失败',
        fields: result.error.flatten().fieldErrors,
      });
    }
    req.body = result.data;
    next();
  };
}

app.post('/users', validate(createUserSchema), createUser);

4. CSRF 防护

const crypto = require('crypto');

function csrfTokenMiddleware(req, res, next) {
  if (!req.cookies['csrf-token']) {
    const token = crypto.randomBytes(32).toString('hex');
    res.cookie('csrf-token', token, {
      httpOnly: false,
      secure: process.env.NODE_ENV === 'production',
      sameSite: 'strict',
    });
  }
  next();
}

function csrfVerify(req, res, next) {
  const safeMethods = ['GET', 'HEAD', 'OPTIONS'];
  if (safeMethods.includes(req.method)) return next();
  const cookieToken = req.cookies['csrf-token'];
  const headerToken = req.headers['x-csrf-token'];
  if (!cookieToken || cookieToken !== headerToken) {
    return res.status(403).json({ error: 'CSRF token 无效' });
  }
  next();
}

5. SQL 注入防御

// 禁止:字符串拼接
// 错误: db.query(`SELECT * FROM users WHERE id = ${req.params.id}`)

// 正确:参数化查询
const { Pool } = require('pg');
const pool = new Pool();

app.get('/users/:id', async (req, res) => {
  const { rows } = await pool.query(
    'SELECT id, name, email FROM users WHERE id = $1',
    [req.params.id]
  );
  res.json(rows[0] ?? null);
});

6. 安全检查清单

方面措施工具包
HTTP 头设置安全头helmet
限流节流请求express-rate-limit
输入验证与清洗zod / joi
密码bcrypt 哈希bcryptjs
CORS白名单来源cors
CSRF双提交 Cookie自定义
SQL参数化查询ORM / pg
密钥使用环境变量dotenv