常见安全威胁
Node.js 作为后端运行时环境,面临多种安全威胁。从代码注入到依赖漏洞,开发者需全面了解攻击场景并采取针对性防护措施。以下分类讨论典型风险及解决方案。
代码注入攻击
代码注入是Node.js应用最危险的安全威胁之一。常见形式包括:
- SQL注入:未过滤的用户输入直接拼接SQL语句
// 危险示例
const query = `SELECT * FROM users WHERE username = '${req.body.username}'`;
db.query(query, (err, result) => { ... });
// 安全方案
const query = 'SELECT * FROM users WHERE username = ?';
db.query(query, [req.body.username], (err, result) => { ... });
- 命令注入:通过child_process执行未净化的输入
// 危险示例
const { exec } = require('child_process');
exec(`ls ${userInput}`, (error, stdout) => {...});
// 安全方案
const { execFile } = require('child_process');
execFile('ls', [userInput], (error, stdout) => {...});
- 模板注入:动态模板引擎处理用户输入
// EJS示例
const template = `<h1>Welcome <%= unsafeUserInput %></h1>`;
// 应使用内容转义
const template = `<h1>Welcome <%- escape(unsafeUserInput) %></h1>`;
身份认证缺陷
认证系统漏洞常导致严重安全问题:
- 弱密码策略:未强制密码复杂度要求
// 应使用密码强度校验库
const zxcvbn = require('zxcvbn');
if (zxcvbn(password).score < 3) {
throw new Error('密码强度不足');
}
- 会话固定攻击:登录前后session ID不变
// 应在认证成功后重置会话
app.post('/login', (req, res) => {
req.session.regenerate(() => {
req.session.user = authenticatedUser;
});
});
- JWT实现问题:未验证签名或使用弱算法
// 错误配置
jwt.sign(payload, 'weak-secret', { algorithm: 'HS256' });
// 正确做法
jwt.sign(payload, process.env.STRONG_SECRET, {
algorithm: 'RS256',
expiresIn: '2h'
});
依赖链风险
第三方包可能引入安全隐患:
- 恶意包植入:通过依赖项执行恶意代码
# 应定期审计依赖
npm audit
npx nodejsscan
- 过时依赖漏洞:未及时更新存在已知漏洞的包
// package.json应指定精确版本
"dependencies": {
"express": "4.18.2",
"lodash": "4.17.21"
}
- 供应链攻击:开发者账户被盗导致合法包被篡改
// 使用包完整性校验
npm install --package-lock-only
npm ci
配置错误
不当配置常成为攻击入口:
- 敏感信息泄露:配置文件包含明文密码
// 错误做法
module.exports = {
dbPassword: '123456'
};
// 应使用环境变量
require('dotenv').config();
module.exports = {
dbPassword: process.env.DB_PASSWORD
};
- CORS过度宽松:允许任意来源访问
// 危险配置
app.use(cors());
// 安全配置
const whitelist = ['https://example.com'];
app.use(cors({
origin: (origin, callback) => {
if (whitelist.indexOf(origin) !== -1) {
callback(null, true)
} else {
callback(new Error('Not allowed by CORS'))
}
}
}));
- HTTP头缺失:未设置安全相关头部
// 应添加安全头部
const helmet = require('helmet');
app.use(helmet());
拒绝服务攻击
系统资源可能被恶意耗尽:
- 正则表达式灾难:低效正则导致CPU过载
// 危险正则
/(a+)+$/.test('aaaaaaaaX');
// 应使用安全库
const safe-regex = require('safe-regex');
if (!safeRegex(regexPattern)) {
throw new Error('潜在危险的正则表达式');
}
- 未限制请求体大小:大型文件上传导致内存溢出
// 应配置body解析限制
app.use(express.json({ limit: '100kb' }));
app.use(express.urlencoded({ limit: '100kb' }));
- 事件循环阻塞:同步操作占用主线程
// 错误示例
const data = fs.readFileSync('large.file');
// 正确做法
fs.readFile('large.file', (err, data) => {...});
文件系统安全
不当文件操作导致漏洞:
- 路径遍历攻击:未过滤用户提供的文件路径
// 危险示例
app.get('/download', (req, res) => {
const file = req.query.file;
res.download(`/var/www/uploads/${file}`);
});
// 安全方案
const path = require('path');
app.get('/download', (req, res) => {
const file = path.basename(req.query.file);
const fullPath = path.join('/var/www/uploads', file);
if (!fullPath.startsWith('/var/www/uploads')) {
return res.status(403).end();
}
res.download(fullPath);
});
- 临时文件竞争:文件创建与权限设置不同步
// 不安全操作
fs.writeFile('/tmp/data.tmp', data, (err) => {
fs.chmod('/tmp/data.tmp', 0o600, (err) => {...});
});
// 原子操作方案
const { open } = require('fs').promises;
async function safeWrite(path, data) {
const fd = await open(path, 'wx', 0o600);
await fd.write(data);
await fd.close();
}
日志与监控缺陷
安全事件未被有效记录:
- 敏感信息记录:日志中包含密码或令牌
// 错误日志
console.log(`User login with password: ${password}`);
// 应过滤敏感字段
const maskFields = (obj) => {
const masked = { ...obj };
['password', 'token'].forEach(field => {
if (masked[field]) masked[field] = '***';
});
return masked;
};
- 日志注入攻击:恶意输入污染日志格式
// 危险示例
logger.info(`User input: ${userInput}`);
// 安全处理
const sanitize = (str) => str.replace(/\n/g, '\\n');
logger.info(`User input: ${sanitize(userInput)}`);
- 缺乏监控:未设置异常行为告警
// 应配置异常监控
const NewRelic = require('newrelic');
process.on('uncaughtException', (err) => {
NewRelic.noticeError(err);
process.exit(1);
});
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn