Skip to content

ExpressJS - 安全实践

Express.js 是一个用于 Node.js 的简约且不固执己见的 Web 框架,为 Web 和移动应用程序提供了一套强大的功能。它的灵活性意味着安全并非默认内置;实施安全是开发者的责任。本指南涵盖了构建安全且可用于生产环境的 Express 应用程序的基本安全最佳实践。

始终使用 Express 及其依赖项的最新稳定版本。过时的软件包可能包含已知的漏洞,攻击者可以利用这些漏洞。定期审计您项目的依赖项。

npm 命令行界面(CLI)有一个内置命令,用于分析您的依赖树是否存在漏洞。

# 在项目根目录中运行此命令
npm audit
# 自动安装易受攻击依赖项的兼容更新
npm audit fix

使用 Snyk 和 GitHub Dependabot 实现自动化安全

Section titled “使用 Snyk 和 GitHub Dependabot 实现自动化安全”

为了持续的安全监控,请集成 Snyk 或 GitHub 原生工具 Dependabot 等服务。这些工具会扫描您的仓库,检测易受攻击的依赖项,并可以自动创建拉取请求来更新它们。

helmet 是一个关键的中间件,它设置各种 HTTP 头部,以帮助保护您的应用程序免受常见的 Web 漏洞(如跨站脚本 (XSS)、点击劫持 (clickjacking) 和其他攻击)的侵害。它是由 15 个较小的中间件函数组成的集合,用于设置安全相关的 HTTP 响应头部。

# 安装
npm install helmet
# 使用
import express from 'express';
import helmet from 'helmet';
const app = express();
// 使用 helmet 的安全默认配置
app.use(helmet());
// ... 您的其他应用程序配置

默认情况下,helmet 启用广泛的保护,包括用于强制 HTTPS 的 Strict-Transport-Security (HSTS),用于点击劫持保护的 X-Frame-Options,以及用于缓解 XSS 攻击的强大 Content-Security-Policy (CSP)。您可以根据应用程序的需求自定义策略。

永远不要信任用户提供的数据。恶意输入是 SQL 注入、NoSQL 注入和跨站脚本 (XSS) 等攻击的主要载体。始终验证和清理来自 req.body、req.params 和 req.query 的数据。

express-validator 库为此目的提供了一套强大的工具。

# 安装
npm install express-validator
# 示例:用户注册路由
import { body, validationResult } from 'express-validator';
app.post(
'/register',
// 验证链
body('email').isEmail().normalizeEmail(),
body('password').isLength({ min: 8 }).withMessage('Password must be at least 8 characters long'),
body('username').not().isEmpty().trim().escape(),
(req, res) => {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() });
}
// 如果验证通过,则继续创建用户
// `username` 现在已被清理(例如,'<script>' 变为 '&lt;script&gt;')
// ...
}
);

通过限制用户在给定时间范围内可以发出的请求数量,保护您的应用程序免受暴力破解(brute-force)攻击和拒绝服务(DoS)攻击。express-rate-limit 中间件非常适合此目的。

# 安装
npm install express-rate-limit
# 使用
import rateLimit from 'express-rate-limit';
const apiLimiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 分钟
max: 100, // 将每个 IP 的请求限制为每个时间窗口 100 个
standardHeaders: true, // 在 `RateLimit-*` 头部中返回速率限制信息
legacyHeaders: false, // 禁用 `X-RateLimit-*` 头部
message: 'Too many requests from this IP, please try again after 15 minutes',
});
// 将速率限制中间件应用于所有 API 请求
app.use('/api', apiLimiter);

专业提示: 默认的内存存储不适用于具有多个服务器实例的生产环境。对于可扩展的解决方案,请使用像 Redis 这样的外部存储。请参阅 express-rate-limit 文档以了解存储实现。

在生产环境中,始终通过 HTTPS 提供您的应用程序服务。这会加密传输中的数据,保护其免受窃听和篡改。虽然您可以在 Node.js 中直接配置 HTTPS,但现代的最佳实践是在反向代理或负载均衡器(例如 Nginx、Caddy 或您的云提供商的负载均衡器)处终止 SSL/TLS。这减轻了加密的 CPU 密集型工作,并简化了您的应用程序代码。

如果您的应用程序位于反向代理之后,它可能会通过 HTTP 接收请求。使用 x-forwarded-proto 头部来确定原始协议,并在必要时进行重定向。此外,信任代理。

// 信任第一个代理
app.set('trust proxy', 1);
app.use((req, res, next) => {
if (process.env.NODE_ENV === 'production' && !req.secure) {
// 将 http 重定向到 https
return res.redirect('https://' + req.headers.host + req.url);
}
next();
});

如果您使用会话(sessions),请安全地配置它们以防止会话劫持和信息泄露。使用带有安全设置的 express-session 中间件。

# 安装
npm install express-session
# 使用
import session from 'express-session';
// 对于生产环境,请使用适当的会话存储,例如 connect-redis
app.use(session({
secret: process.env.SESSION_SECRET, // 从环境变量加载密钥
resave: false,
saveUninitialized: false,
cookie: {
secure: process.env.NODE_ENV === 'production', // 仅通过 HTTPS 发送 cookie
httpOnly: true, // 防止客户端 JS 访问 cookie
sameSite: 'lax', // 防止 CSRF 攻击
maxAge: 24 * 60 * 60 * 1000 // Cookie 过期时间(毫秒,例如 24 小时)
}
}));

重要提示: 永远不要硬编码像 SESSION_SECRET 这样的秘密。使用环境变量(例如,通过 .env 文件和 dotenv 包)来管理敏感配置。

通用且设计良好的错误处理器可以防止将敏感的应用程序详细信息(如堆栈跟踪)泄露给客户端。

// 集中式错误处理中间件
app.use((err, req, res, next) => {
console.error(err.stack);
// 在生产环境中避免向客户端发送堆栈跟踪
if (process.env.NODE_ENV === 'production') {
res.status(500).json({ message: 'Something went wrong!' });
} else {
res.status(500).json({
message: err.message,
stack: err.stack
});
}
});

通过遵循这些实践,您可以为 Express 应用程序建立坚实的安全基础。安全是一个持续的过程,因此请随时了解新的威胁并保持依赖项的更新。