• 如何在node.js中避免命令行注入


    我们将学习一下如何使用正确的姿势通过node调用系统命令,从而避免命令注入的安全漏洞。
    通常我们调用系统命令使用非常典型的方式child_process.exec来执行,通过一个命令字符串,它将回调返回一个错误或命令的结果。
    代码如下:

    child_process.exec('ls', function (err, data) {
        console.log(data);
    });
    
    • 1
    • 2
    • 3

    但是假如你的命令行需要依赖用户输入该怎么办?显而易见的解决方案是把用户输入的值作为你命令的一部分,使用字符串拼接的方式来构建你的整条命令。
    代码如下:

    var path = "user input";
    child_process.exec('ls -l ' + path, function (err, data) {
        console.log(data);
    });
    
    • 1
    • 2
    • 3
    • 4

    有没有闻到很坏的味道?
    我们都知道,严谨、安全的程序应该不要相信任何输入源,因为这样有可能会引入不是你想要的可执行代码(注入代码)。
    以这里的例子为例,在执行child_process.exec的时候,底层其实是调用/ bin/sh,而不是目标程序。也就是说,发送的命令就会传递到/bin/sh 进程作为shell命令。child_process.exec有一个误导性的名称:——bash的解析器,而不是启动器。这意味着只要用户输入的命令是shell支持的,都可以在系统级别执行,当然也可能会含有破坏性的命令。

    系统调用

    [pid 25170] execve(“/bin/sh”, [“/bin/sh”, “-c”, “ls -l user input”], [/* 16 vars */]
    例如,攻击者可以使用;来结束语句,并开始另一个命令,他们还可以使用引号或者$()来运行子命令等等很多潜在的危险。

    那么正确的姿势是怎样的呢?
    execFile / spawn
    调用spawn 和 execFile这样的接口,将所有的命令都作为额外参数数组,而不是直接shell环境下执行的命令,另外不要直接操纵原本要运行的命令。

    我们来修改下例子,看看如何使用spawn 和 execFile。

    child_process.execFile
    
    var child_process = require('child_process');
    var path = "."
    child_process.execFile('/bin/ls', ['-l', path], function (err, result) {
        console.log(result)
    });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    系统调用

    [pid 25565] execve("/bin/ls", ["/bin/ls", "-l", "."], [/* 16 vars */]
    child_process.spawn
    
    var child_process = require('child_process');
    var path = "."
    var ls = child_process.spawn('/bin/ls', ['-l', path])
    ls.stdout.on('data', function (data) {
        console.log(data.toString());
    });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    系统调用

    [pid 26883] execve(“/bin/ls”, [“/bin/ls”, “-l”, “.”], [/* 16 vars */
    为什么这种方式不易受到命令注入呢?
    通过系统调用来对比,可以发现我们的目标程序是execve的参数。这意味着用户不能在shell运行子命令,因为/bin/ls解析的时候只会把path当成字符串参数,所以里面的引号或分号都起不了作用。类似于使用参数化解决注入问题的思想,在SQL注入中也经常使用到,SQL查询将查询条件使用问好?进行参数化,而不是通过拼接字符串的方式查询。

    不过使用execFile / spawn并不总是安全的:例如,使用 spawn or execFile运行/bin/find ,并直接通过用户输入,依然可能会导致系统被接管,而使文件被任意读/写。

    几点忠告:

    1. 避免使用child_process.exec, 尤其是命令中包含用户输入的情况。
    2. 如果你必须允许用户输入命令,可以使用命令选项的方式,并且确定哪些选项是安全的,添加到白名单,只有白名单命令才执行,否则全部过滤。(这里不建议使用黑名单,因为黑名单并不一定能够穷举)
  • 相关阅读:
    拨开发展迷雾,将“智慧”嵌入全业务场景【2022戴尔科技峰会预告】
    Handler 原理
    Python算法——最近公共祖先
    CMMI2.0和1.3之间的区别有哪些?
    java计算机毕业设计雁门关风景区宣传网站源程序+mysql+系统+lw文档+远程调试
    100天精通Golang(基础入门篇)——第19天:深入剖析Go语言中方法(Method)的妙用与实践
    自制J-Flash烧录工具——Qt调用jlinkARM.dll方式
    Java实现给PDF文件加文字水印和图片水印(可以自定义水印格式)
    [Spring Framework]AOP配置管理②(AOP通知类型)
    背包问题(01背包、完全背包、多重背包)
  • 原文地址:https://blog.csdn.net/jgku/article/details/128210810