您现在的位置是:网站首页 > 常见安全威胁文章详情
常见安全威胁
陈川
【
Node.js
】
45681人已围观
6072字
Node.js作为流行的JavaScript运行时环境,广泛应用于后端开发。然而,其生态系统的开放性也带来了多种安全威胁,开发者需警惕这些风险并采取防护措施。
注入攻击
注入攻击是Node.js应用中最常见的安全威胁之一。攻击者通过构造恶意输入,使应用执行非预期的操作。SQL注入和命令注入是两种主要形式。
SQL注入示例:
const query = `SELECT * FROM users WHERE username = '${req.body.username}'`;
db.query(query, (err, result) => {
// 攻击者可输入 ' OR '1'='1 绕过验证
});
防护方案应使用参数化查询:
const query = 'SELECT * FROM users WHERE username = ?';
db.query(query, [req.body.username], (err, result) => {
// 安全查询
});
命令注入通常发生在调用系统命令时:
const { exec } = require('child_process');
exec(`ls ${req.query.dir}`, (err, stdout) => {
// 攻击者可输入 ; rm -rf / 执行危险命令
});
应使用白名单验证输入:
const validDirs = ['/tmp', '/var'];
if (!validDirs.includes(req.query.dir)) {
return res.status(400).send('Invalid directory');
}
跨站脚本攻击(XSS)
XSS攻击允许攻击者在用户浏览器中执行恶意脚本。Node.js应用中常见于未正确转义的动态内容。
反射型XSS示例:
app.get('/search', (req, res) => {
res.send(`<p>搜索结果: ${req.query.q}</p>`);
// 攻击者可构造 ?q=<script>恶意代码</script>
});
防护措施包括内容安全策略(CSP)和转义输出:
const escapeHtml = require('escape-html');
app.get('/search', (req, res) => {
res.send(`<p>搜索结果: ${escapeHtml(req.query.q)}</p>`);
});
存储型XSS更危险,恶意脚本被持久化到数据库:
// 用户评论存储
comments.push({
text: req.body.text, // 可能包含<script>alert(1)</script>
user: req.user.id
});
敏感数据泄露
配置不当可能导致敏感信息如API密钥、数据库凭证泄露。常见问题包括:
- 将.env文件提交到版本控制
- 在错误日志中记录敏感数据
- 响应中包含过多信息
错误示例:
// config.js
module.exports = {
dbPassword: 'supersecret', // 硬编码密码
apiKey: '12345'
};
正确做法应使用环境变量:
// config.js
module.exports = {
dbPassword: process.env.DB_PASSWORD,
apiKey: process.env.API_KEY
};
不安全的依赖
Node.js应用通常依赖大量第三方包,可能引入漏洞。
检查依赖漏洞的方法:
npm audit
应定期更新依赖:
npm outdated
npm update
对于关键应用,可锁定依赖版本:
{
"dependencies": {
"express": "4.17.1" // 精确版本而非^4.17.1
}
}
身份验证缺陷
不正确的身份验证实现会导致未授权访问。
常见问题包括:
- 弱密码策略
- 会话固定
- JWT实现不当
不安全的路由保护示例:
app.get('/admin', (req, res) => {
if (req.cookies.loggedIn === 'true') { // 容易伪造
res.send('管理员面板');
}
});
应使用成熟的认证库如Passport.js:
const passport = require('passport');
app.get('/admin',
passport.authenticate('jwt', { session: false }),
(req, res) => {
res.send('管理员面板');
}
);
拒绝服务(DoS)攻击
Node.js单线程特性使其易受DoS攻击。
常见攻击向量:
- 正则表达式灾难性回溯
- 未限制的请求体大小
- 未超时的长时间操作
易受攻击的正则示例:
app.post('/validate', (req, res) => {
const regex = /^([a-zA-Z0-9]+)*$/; // 灾难性回溯
const isValid = regex.test(req.body.input);
res.json({ valid: isValid });
});
防护措施包括:
- 限制请求体大小:
app.use(express.json({ limit: '10kb' }));
- 设置请求超时:
const timeout = require('connect-timeout');
app.use(timeout('5s'));
不安全的文件操作
文件系统操作不当可能导致目录遍历等攻击。
漏洞示例:
app.get('/download', (req, res) => {
const file = path.join(__dirname, 'files', req.query.file);
res.download(file); // 攻击者可请求 ../../etc/passwd
});
应验证文件路径:
const allowedPath = path.join(__dirname, 'files');
const requestedPath = path.join(__dirname, 'files', req.query.file);
if (!requestedPath.startsWith(allowedPath)) {
return res.status(403).send('禁止访问');
}
HTTP头部安全配置
不当的HTTP头部可能导致安全风险。关键头部包括:
- X-XSS-Protection
- X-Content-Type-Options
- Strict-Transport-Security
- Content-Security-Policy
配置示例:
const helmet = require('helmet');
app.use(helmet());
// 或手动配置
app.use((req, res, next) => {
res.setHeader('X-Frame-Options', 'DENY');
res.setHeader('X-Content-Type-Options', 'nosniff');
next();
});
不安全的反序列化
JSON解析可能被滥用执行代码。
危险示例:
const data = JSON.parse(req.body, (key, value) => {
if (key === '__proto__') return {}; // 原型污染防护
return value;
});
应避免使用复杂的reviver函数,并验证输入结构。
服务器端请求伪造(SSRF)
应用发起不可信的HTTP请求可能导致内部网络探测。
漏洞示例:
app.get('/fetch', async (req, res) => {
const response = await axios.get(req.query.url); // 可能访问内部服务
res.send(response.data);
});
防护措施:
- 限制目标URL协议和域名
- 使用DNS重绑定防护
- 设置请求超时
const { URL } = require('url');
app.get('/fetch', async (req, res) => {
const parsedUrl = new URL(req.query.url);
if (!parsedUrl.hostname.endsWith('.example.com')) {
return res.status(400).send('Invalid URL');
}
// 安全请求
});
日志注入
未处理的日志内容可能破坏日志系统或注入恶意内容。
漏洞示例:
app.use((req, res, next) => {
console.log(`Request from ${req.ip}`); // 可能包含换行符
next();
});
应清理日志内容:
const clean = (str) => str.replace(/\n/g, '\\n').replace(/\r/g, '\\r');
app.use((req, res, next) => {
console.log(`Request from ${clean(req.ip)}`);
next();
});
不安全的随机数生成
Math.random()
不适用于安全场景。
不安全示例:
function generateToken() {
return Math.random().toString(36).substr(2); // 可预测
}
应使用加密安全的随机数:
const crypto = require('crypto');
function generateToken() {
return crypto.randomBytes(32).toString('hex');
}
原型污染
通过修改__proto__可能改变对象行为。
漏洞示例:
function merge(target, source) {
for (const key in source) {
target[key] = source[key]; // 可能污染原型
}
}
防护方案:
function safeMerge(target, source) {
for (const key in source) {
if (key !== '__proto__' && key !== 'constructor') {
target[key] = source[key];
}
}
}
不安全的子进程创建
不当的子进程调用可能导致命令注入。
危险示例:
const { exec } = require('child_process');
exec(`git clone ${req.body.repo}`, (err) => {
// 可能注入额外命令
});
应使用分离参数:
const { spawn } = require('child_process');
const child = spawn('git', ['clone', req.body.repo]);
未验证的重定向
开放重定向可能被用于钓鱼攻击。
漏洞示例:
app.get('/redirect', (req, res) => {
res.redirect(req.query.url); // 可能指向恶意网站
});
应验证目标域名:
const allowedDomains = new Set(['example.com', 'trusted.org']);
app.get('/redirect', (req, res) => {
const url = new URL(req.query.url);
if (!allowedDomains.has(url.hostname)) {
return res.status(400).send('Invalid redirect');
}
res.redirect(url.toString());
});