Node.js中安全调用系统命令的方法(避免注入安全漏洞)

在这篇文章中,我们将学习正确使用Node.js调用系统命令的方法,以避免常见的命令行注入漏洞。

我们经常使用的调用命令的方法是最简单的child_process.exec。它有很一个简单的使用模式;通过传入一段字符串命令,并把一个错误或命令处理结果回传至回调函数中。

这里是你通过child_process.exec调用系统命令一个非常典型的例子。

代码如下:

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

不过,当你需要在你调用的命令中添加一些用户输入的参数时,会发生什么?显而易见的解决方案是把用户输入直接和您的命令进行字符串合并。但是,我多年的经验告诉我:当你将连接的字符串从一个系统发送到另一个系统时,总有一天会出问题。

代码如下:

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

为什么连接字符串会出问题?

嗯,因为在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环境下可以执行其他命令的参数,并不会运行额外的命令。

让我们使用的execFile和spawn修改一下之前的例子,看看系统调用有何不同,以及为什么它不容易受到命令注入。

child_process.execFile

代码如下:

var child_process = require('child_process');

var path = "."
child_process.execFile('/bin/ls', ['-l', path], function (err, result) {
    console.log(result)
});

运行的系统调用

代码如下:

[pid 25565] execve("/bin/ls", ["/bin/ls", "-l", "."], [/* 16 vars */]

child_process.spawn

使用 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());
});

运行的系统调用

代码如下:

[pid 26883] execve("/bin/ls", ["/bin/ls", "-l", "."], [/* 16 vars */

当使用spawn或execfile时,我们的目标是只执行一个命令(参数)。这意味着用户不能运行注入的命令,因为/bin/ls并不知道如何处理反引号或pipe或;。它的/bin/bash将要解释的是那些命令的参数。它类似于使用将参数传入SQL查询(parameter),如果你熟悉的话。

但还需要警告的是:使用spawn或execFile并不总是安全的。例如,运行 /bin/find,并传入用户输入参数仍有可能导致系统被攻陷。 find命令有一些选项,允许读/写任意文件。

所以,这里有一些关于Node.js运行系统命令的指导建议:

避免使用child_process.exec,当需要包含用户输入的参数时更是如此,请牢记。
尽量避免让用户传入参数,使用选择项比让用户直接输入字符串要好得多。
如果你必须允许用户输入参数,请广泛参考该命令的参数,确定哪些选项是安全的,并建立一个白名单。

(0)

相关推荐

  • 基于NodeJS的前后端分离的思考与实践(四)安全问题解决方案

    前言 在前后端分离的开发模式中,从开发的角色和职能上来讲,一个最明显的变化就是:以往传统中,只负责浏览器环境中开发的前端同学,需要涉猎到服务端层面,编写服务端代码.而摆在面前的一个基础性问题就是如何保障Web安全? 本文就在前后端分离模式的架构下,针对前端在Web开发中,所遇到的安全问题以及应对措施和注意事项,并提出解决方案. 跨站脚本攻击(XSS)的防御 问题及解决思路 跨站脚本攻击(XSS,Cross-site scripting)是最常见和基本的攻击Web网站的方法.攻击者可以在网页上发布

  • Node.js中安全调用系统命令的方法(避免注入安全漏洞)

    在这篇文章中,我们将学习正确使用Node.js调用系统命令的方法,以避免常见的命令行注入漏洞. 我们经常使用的调用命令的方法是最简单的child_process.exec.它有很一个简单的使用模式;通过传入一段字符串命令,并把一个错误或命令处理结果回传至回调函数中. 这里是你通过child_process.exec调用系统命令一个非常典型的例子. 复制代码 代码如下: child_process.exec('ls', function (err, data) {     console.log(

  • 在 Node.js 中使用 async 函数的方法

    借助于新版 V8 引擎,Node.js 从 7.6 开始支持 async 函数特性.今年 10 月 31 日,Node.js 8 也开始成为新的长期支持版本,因此你完全可以放心大胆地在你的代码中使用 async 函数了.在这边文章里,我会简要地介绍一下什么是 async 函数,以及它会如何改变我们编写 Node.js 应用的方式. 1 什么是 async 函数 利用 async 函数,你可以把基于 Promise 的异步代码写得就像同步代码一样.一旦你使用 async 关键字来定义了一个函数,那

  • 在 Node.js 中使用原生 ES 模块方法解析

    从版本 8.5.0 开始,Node.js 开始支持原生 ES 模块,可以通过命令行选项打开该功能.新功能很大程度上得归功于 Bradley Farias. 1.演示 这个示例的代码目录结构如下: esm-demo/ lib.mjs main.mjs lib.mjs: export function add(x, y) { return x + y; } main.mjs: import {add} from './lib.mjs'; console.log('Result: '+add(2, 3

  • Node.js中npx命令的使用方法及场景分析

    npx使用教程 今晚在学习Vue-Cli时, 由于突发奇想想试试最新的@4.x.x版本, 但是本地全局安装的脚手架版本是@2.x.x的, 因为不想污染全局于是就想到用npx命令, 一路上踩坑不断, 为了以后能够更好的使用npx并区分其跟npm的指令, 就有了本篇笔记 npm 是从5.2版开始, 增加(自带)了 npx 命令. 如果发现没安装请手动安装: npm i -g npx npm与npx的概念 NPM(Node Package Manager) 是Node.js提供的一个包管理器, 可以使

  • Node.js 中使用 async 函数的方法

    借助于新版 V8 引擎,Node.js 从 7.6 开始支持 async 函数特性.今年 10 月 31 日,Node.js 8 也开始成为新的长期支持版本,因此你完全可以放心大胆地在你的代码中使用 async 函数了.在这边文章里,我会简要地介绍一下什么是 async 函数,以及它会如何改变我们编写 Node.js 应用的方式. 1 什么是 async 函数 利用 async 函数,你可以把基于 Promise 的异步代码写得就像同步代码一样.一旦你使用 async 关键字来定义了一个函数,那

  • 在Node.js中实现文件复制的方法和实例

    Node.js 本身并没有提供直接复制文件的 API,如果想用 Node.js 复制文件或目录,需要借助其他的 API 来实现.复制单个的文件可以直接用 readFile.writeFile,这样比较简便.如果是复制一个目录下的所有文件,目录下可能还包含了子目录,那么此时就需要用到更高级点的 API 了. 流 流是 Node.js 移动数据的方式,Node.js 中的流是可读/可写的,HTTP 和文件系统模块都有用到流.在文件系统中,使用流来读取文件的时候,对于一个大文件可能并不会一次性读取完,

  • node.js中的http.response.end方法使用说明

    方法说明: 结束响应,告诉客户端所有消息已经发送.当所有要返回的内容发送完毕时,该函数必须被调用一次. 如何不调用该函数,客户端将永远处于等待状态. 语法: 复制代码 代码如下: response.end([data], [encoding]) 接收参数: data                           end()执行完毕后要输出的字符,如果指定了 data 的值,那就意味着在执行完 response.end() 之后,会接着执行一条 response.write(data , e

  • node.js中使用socket.io的方法

    使用socket.io的使用创建一个socket.io服务器即可.但是该服务器依赖于一个已经创建的http服务器. 在http服务器运行之后,使用listen方法为该http服务器附加一个socket.io服务器. 复制代码 代码如下: var sio=require("scoket.io"); var socket=sio.listen(server); socket就是在server基础上创建的一个socket.io服务器. 当客户端与服务器端建立连接时,触发socket.io服务

  • node.js中的http.response.write方法使用说明

    方法说明: 向请求的客户端发送响应内容. 在 response.end() 之前,response.write() 可以被执行多次. 语法: 复制代码 代码如下: response.write(chunk, [encoding]) 参数: chunk                      是一个buffer 或 字符串,表示发送的内容 encoding                如果chunk是字符串,就需要指定encoding来说明它的编码方式,默认utf-8 例子: 复制代码 代码如

  • Node.js中sequelize时区的配置方法

    基本概念 我们先来介绍一些可能当年在地理课上学习过的基本概念. 说起来,时间真是一个神奇的东西.以前人们通过观察太阳的位置来决定时间(比如:使用日晷),这就使得不同经纬度的地区时间是不一样的.后来人们进一步规定以子午线为中心,向东西两侧延伸,每 15 度划分一个时区,刚好是 24 个时区.然后因为一天有 24 小时,地球自转一圈是 360 度,360 度 / 24 小时 = 15 度/小时,所以每差一个时区,时间就差一个小时. 最开始的标准时间(子午线中心处的时间)是英国伦敦的皇家格林威治天文台

随机推荐