Express.js 后端安全(使用 Nginx + VPS)
I’m happy to translate the article for you, but I’ll need the full text of the post (the content you’d like translated). Could you please paste the article’s body here? Once I have the text, I’ll provide a Simplified‑Chinese translation while keeping the source line, formatting, markdown, and code blocks unchanged.
目录
- 威胁模型
- 为什么后端安全至关重要
- 安全基线
- 项目设置
- 安全响应头
- CORS 策略
- 身份验证与授权
- 输入验证与清理
- 限流
- 请求大小限制
- 日志与审计
- 密钥与环境安全
- TLS(HTTPS)与 Nginx 配置
- Nginx 限流
- 基础机器人/扫描器削减
- Nginx 推荐的安全响应头
- SSH 加固
- 防火墙(UFW)
- Fail2ban
- 自动安全更新
- 部署检查清单
威胁模型
公共后端的典型攻击向量
- 暴力破解登录尝试
- API 滥用 / 抓取
- 凭证填充
- 注入攻击(SQL/NoSQL)
- CORS 配置错误
- 令牌窃取(JWT、会话 Cookie)
- 漏洞扫描器
- 反向代理绕过(直接端口访问)
- 通过弱 SSH 导致服务器被攻破
影响矩阵
| 威胁 | 影响 |
|---|---|
| NoSQL Injection | 数据库泄露 |
| Credential Stuffing | 账户被接管 |
| JWT Theft | 完全用户冒充 |
| CSRF | 未授权操作 |
| XSS | 令牌和会话泄露 |
| API Abuse | 服务器及支付系统被利用 |
目标是通过使用多层独立防护来降低风险。
安全基线
| 控制项 | 描述 |
|---|---|
| 强制使用 HTTPS | 所有流量均已加密 |
| 强大的身份验证策略 | 基于 JWT 或会话 |
| 严格的 CORS | 白名单来源 |
| 服务器端验证 | 永不信任客户端数据 |
| 限流 | 阻止暴力破解和滥用 |
| 集中式日志记录 | 审计日志 |
| Nginx 反向代理防护 | 隐藏内部端口 |
| 防火墙 + SSH 加固 | 限制攻击面 |
| Fail2ban + 自动安全补丁 | 被动与主动防御 |
项目设置
# Core dependencies
npm i express helmet cors express-rate-limit cookie-parser compression
# Validation, logging, env handling
npm i zod pino pino-http dotenv
安全标头
Helmet 应用了一套合理的 HTTP 安全标头。
import helmet from "helmet";
app.use(helmet());
注意: 对于纯 API,除非您也提供 HTML 页面,否则可以跳过严格的 CSP。
CORS 策略
在生产环境中绝不要使用 *,尤其是涉及 cookie 或凭证时。
import cors from "cors";
const allowedOrigins = [
"https://your-frontend.com",
"https://www.your-frontend.com",
];
app.use(
cors({
origin: (origin, cb) => {
// Allow non‑browser requests (e.g., Postman) or whitelisted origins
if (!origin) return cb(null, true);
if (allowedOrigins.includes(origin)) return cb(null, true);
return cb(new Error("CORS blocked"), false);
},
credentials: true,
methods: ["GET", "POST", "PUT", "PATCH", "DELETE"],
allowedHeaders: ["Content-Type", "Authorization"],
})
);
身份验证与授权
| 概念 | 描述 |
|---|---|
| Authentication | 证明客户端的身份(例如,登录,JWT 发放)。 |
| Authorization | 确定已认证身份可以执行的操作(基于角色的检查)。 |
最佳实践
- 短期访问令牌,轮换刷新令牌。
- 对特权路由进行基于角色的检查。
// Example role guard
export function requireRole(...roles) {
return (req, res, next) => {
if (!req.user || !roles.includes(req.user.role)) {
return res.status(403).json({ status: false, message: "Forbidden" });
}
next();
};
}
输入验证与清理
优先使用 模式验证(例如 Zod)而不是临时检查。
import { z } from "zod";
const createUserSchema = z.object({
name: z.string().min(2).max(80),
email: z.string().email(),
password: z.string().min(8).max(72),
});
export function validate(schema) {
return (req, res, next) => {
const result = schema.safeParse(req.body);
if (!result.success) {
return res.status(400).json({
status: false,
message: "Validation failed",
errors: result.error.issues,
});
}
req.body = result.data; // use sanitized data downstream
next();
};
}
用法
app.post("/api/users", validate(createUserSchema), (req, res) => {
// req.body is guaranteed to match the schema
});
限流
对每个端点类别应用不同的限制。
import rateLimit from "express-rate-limit";
export const globalLimiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 分钟
max: 600, // 每个窗口每个 IP 600 次请求
standardHeaders: true,
legacyHeaders: false,
});
export const authLimiter = rateLimit({
windowMs: 15 * 60 * 1000,
max: 20, // 对登录/注册更严格
message: { message: "Too many login attempts. Try again later." },
});
app.use(globalLimiter); // 应用于所有路由
app.use("/api/auth", authLimiter); // 仅限认证路由
请求大小限制
防止负载滥用:
app.use(express.json({ limit: "200kb" }));
app.use(express.urlencoded({ extended: true, limit: "200kb" }));
日志记录与审计
结构化日志使分析更容易。
import pino from "pino";
import pinoHttp from "pino-http";
const logger = pino({ level: process.env.LOG_LEVEL || "info" });
app.use(pinoHttp({ logger }));
记录(但 绝不要 记录密码、令牌或原始密钥):
- 认证失败
- 可疑的流量激增
- 限流阻止
密钥与环境安全
- 在开发时将密钥存储在
.env中,并且在生产环境中将其存储在服务器的环境变量中。 - 绝不要将
.env提交到版本控制。 - 立即轮换已泄露的密钥。
# .env (example)
NODE_ENV=production
PORT=3000
JWT_SECRET=YOUR-JWT-SECRET-KEY
TLS(HTTPS) & Nginx 反向代理
架构
Internet → Nginx (443) → Express (127.0.0.1:8000)
安装 Certbot 并获取证书
sudo apt update
sudo apt install nginx certbot python3-certbot-nginx -y
# Issue a certificate for your API sub‑domain
sudo certbot --nginx -d api.yourdomain.com
# Verify auto‑renewal timer
sudo systemctl status certbot.timer
Nginx 限流
将以下内容添加到 /etc/nginx/nginx.conf(位于 http {} 块内部):
limit_req_zone $binary_remote_addr zone=api_limit:10m rate=10r/s;
limit_conn_zone $binary_remote_addr zone=conn_limit:10m;
示例 Server 块
server {
listen 443 ssl http2;
server_name api.yourdomain.com;
# TLS (handled by Certbot)
ssl_certificate /etc/letsencrypt/live/api.yourdomain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/api.yourdomain.com/privkey.pem;
# Rate limiting
limit_req zone=api_limit burst=20 nodelay;
limit_conn conn_limit 20;
location / {
proxy_pass http://localhost:3000;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# Timeouts
proxy_connect_timeout 10s;
proxy_send_timeout 30s;
proxy_read_timeout 30s;
}
}
基本的机器人/扫描器减速
在 Nginx 级别阻止常见的垃圾请求。
# Block WordPress xmlrpc pingbacks (if you don't run WP)
location = /xmlrpc {
deny all;
}
根据需要添加更多模式(例如 /.env、/admin、已知的扫描器 User‑Agent)。
推荐的 Nginx 安全响应头
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "DENY" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "no-referrer-when-downgrade" always;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
add_header Permissions-Policy "geolocation=(), microphone=()" always;
# (CSP can be added if you serve HTML)
SSH 加固
# Disable root login
sudo sed -i 's/PermitRootLogin yes/PermitRootLogin no/' /etc/ssh/sshd_config
# Use key‑based auth only
sudo sed -i 's/PasswordAuthentication yes/PasswordAuthentication no/' /etc/ssh/sshd_config
# Change default port (optional)
# sudo sed -i 's/#Port 22/Port 2222/' /etc/ssh/sshd_config
sudo systemctl restart sshd
- 保持私钥安全。
- 使用密码短语并配合 SSH 代理。
防火墙 (UFW)
sudo ufw default deny incoming
sudo ufw default allow outgoing
# Allow SSH (adjust port if you changed it)
sudo ufw allow 22/tcp # or 2222/tcp if you changed the port
# Allow HTTP/HTTPS (handled by Nginx)
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enable
sudo ufw status verbose
Fail2ban
sudo apt install fail2ban -y
# Basic jail for SSH
cat <<EOF > /etc/fail2ban/jail.local
[sshd]
enabled = true
port = ssh
logpath = %(sshd_log)s
maxretry = 5
EOF
永远不要信任前端。 只信任后端并验证所有内容。
自动安全更新
sudo apt install unattended-upgrades -y
sudo dpkg-reconfigure --priority=low unattended-upgrades
Deployment Checklist
- 通过 Nginx + Certbot 强制使用 HTTPS
- 已应用 Helmet 安全头
- 严格的 CORS 白名单
- 使用 Zod(或类似库)进行输入验证
- 限流(Express 与 Nginx)
- 已配置请求大小限制
- 结构化日志(pino)
- 将机密安全存储,且不放入版本控制系统
- 仅使用 SSH 密钥认证,禁用 root 登录
- 已应用 UFW 防火墙规则
- 为 SSH(以及可选的 Nginx)启用 Fail2ban
- 已启用自动安全更新
最终思考
Express.js 后端安全是一门学科,而不是单一的配置或模块。强大的应用层限制、加固的反向代理以及适当锁定的服务器环境是实际安全的组成部分。
通过在 Express、Nginx 和 VPS 级别的加固中实现分层防御,您可以大幅降低 API 的攻击面,保护用户、数据和业务逻辑免受真实世界的威胁。
对于长期的生产使用,安全的后端不仅更安全,而且更可靠、可扩展且值得信赖。