安全不是可选项
2025 年 OWASP Top 10 里,失效的访问控制、注入攻击、加密失败仍然是最高频的安全威胁。对于中小网站来说,攻击者不会因为你规模小就放过你——自动扫描脚本不在乎目标是谁。
好消息是:Node.js 内置模块足以构建坚固的安全防线。
第一道防线:密码哈希
永远不要存储明文密码。SHA-256 也不够——因为 GPU 可以每秒计算数十亿次 SHA-256,暴力破解成本太低。
正确做法是用 scrypt——专门设计来对抗暴力破解的密钥派生函数:
const crypto = require('crypto');
function hashPassword(password) {
const salt = crypto.randomBytes(16).toString('hex');
const hash = crypto.scryptSync(password, salt, 64).toString('hex');
return `${salt}:${hash}`;
}
function verifyPassword(password, stored) {
const [salt, hash] = stored.split(':');
return hash === crypto.scryptSync(password, salt, 64).toString('hex');
}
scrypt 的设计目标是「内存密集」——每次哈希需要分配大量内存,这让 GPU 并行暴力破解变得极度昂贵。
第二道防线:XSS 防护
任何来自用户输入的数据,写入数据库之前都要转义:
function esc(str) {
return String(str)
.replace(/&/g, '&')
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/"/g, '"')
.replace(/'/g, ''');
}
注意:不是在输出时转义,而是在存储前转义。这样即使前端忘了转义,数据库里存的就是安全的。
第三道防线:速率限制
没有速率限制的 API 是免费算力——攻击者可以无限次尝试密码,或把服务器当代理。
const rateLimit = new Map();
const RATE_LIMIT = 60;
const RATE_WINDOW = 60000; // 1 分钟
function checkRate(ip) {
const now = Date.now();
const r = rateLimit.get(ip);
if (!r || now - r.reset > RATE_WINDOW) {
rateLimit.set(ip, { count: 1, reset: now });
return true;
}
r.count++;
return r.count <= RATE_LIMIT;
}
每 IP 每分钟 60 请求——正常用户不可能达到,但脚本一秒就能冲破。
第四道防线:原子写入
JSON 文件作数据库,最大的风险是写入一半进程崩溃导致数据损坏。解决方案来自操作系统:
function writeJSON(filename, data) {
const p = path.join(DATA_DIR, filename);
fs.writeFileSync(p + '.tmp', JSON.stringify(data));
fs.renameSync(p + '.tmp', p);
}
rename 是原子系统调用。要么完整成功,要么完全不变。不存在中间状态。
第五道防线:目录遍历防护
攻击者可能会尝试通过 URL 访问服务器上的任意文件:
GET /../../../etc/passwd
防护很简单——检查解析后的路径是否在项目根目录内:
const ROOT_SAFE = ROOT + path.sep;
if (!fullPath.startsWith(ROOT_SAFE)) {
return send(res, 403, { error: 'Forbidden' });
}
安全响应头
最后补上几个 HTTP 头,花 3 行代码挡住大量常见攻击:
res.setHeader('X-Content-Type-Options', 'nosniff');
res.setHeader('X-Frame-Options', 'DENY');
res.setHeader('Referrer-Policy', 'strict-origin-when-cross-origin');
小结
零依赖不等于零安全。Node.js 的 crypto、fs、zlib 模块 + 正确的架构设计 = 完全可以达到生产级安全性。关键不在于用了多少库,在于是否理解每条防线背后的原理。