不受控命令行¶
ID: js/command-line-injection
Kind: path-problem
Security severity: 9.8
Severity: error
Precision: high
Tags:
- correctness
- security
- external/cwe/cwe-078
- external/cwe/cwe-088
Query suites:
- javascript-code-scanning.qls
- javascript-security-extended.qls
- javascript-security-and-quality.qls
将不受信任的用户输入直接传递给 child_process.exec
或类似执行 Shell 命令的 API 的代码允许用户执行恶意代码。
建议¶
如果可能,请使用不运行 Shell 命令并且将命令参数作为字符串数组而不是单个连接字符串接受的 API。这既更安全,也更便携。
如果以单个字符串形式提供参数,请避免简单地根据空格拆分字符串。参数可能包含引号括起来的空格,导致它们被拆分为多个参数。请使用像 shell-quote
这样的库来将字符串解析为参数数组。
如果此方法不可行,请添加代码以在使用用户输入字符串之前验证其安全性。
示例¶
以下示例展示了从可能包含不受信任数据的 HTTP 查询参数中提取文件名,然后将其嵌入 Shell 命令中以计算其行数,而没有先进行检查。
var cp = require("child_process"),
http = require('http'),
url = require('url');
var server = http.createServer(function(req, res) {
let file = url.parse(req.url, true).query.path;
cp.execSync(`wc -l ${file}`); // BAD
});
恶意用户可以利用这段代码执行任意 Shell 命令。例如,通过提供像 foo.txt; rm -rf .
这样的文件名,用户可以先计算 foo.txt
中的行数,然后删除当前目录中的所有文件。
为了避免这种灾难性的行为,请使用像 child_process.execFileSync
这样的 API,它默认情况下不会生成 Shell。
var cp = require("child_process"),
http = require('http'),
url = require('url');
var server = http.createServer(function(req, res) {
let file = url.parse(req.url, true).query.path;
cp.execFileSync('wc', ['-l', file]); // GOOD
});
如果您想允许用户为 wc
指定其他选项,您可以使用像 shell-quote
这样的库来将用户输入解析为参数数组,而不会冒命令注入的风险。
var cp = require("child_process"),
http = require('http'),
url = require('url'),
shellQuote = require('shell-quote');
var server = http.createServer(function(req, res) {
let options = url.parse(req.url, true).query.options;
cp.execFileSync('wc', shellQuote.parse(options)); // GOOD
});
或者,可以通过在使用文件名之前检查其是否在允许的字符列表中来使原始示例变得安全。
var cp = require("child_process"),
http = require('http'),
url = require('url');
var server = http.createServer(function(req, res) {
let file = url.parse(req.url, true).query.path;
// only allow safe characters in file name
if (file.match(/^[\w\.\-\/]+$/)) {
cp.execSync(`wc -l ${file}`); // GOOD
}
});
参考文献¶
OWASP: 命令注入.
npm: shell-quote.
常见弱点枚举:CWE-78.
常见弱点枚举:CWE-88.