目录导航
NoSQL注入简介
NoSQL注入漏洞使攻击者可以将代码注入到不使用SQL查询的数据库的命令中,例如MongoDB。NoSQL注入攻击可能特别危险,因为代码已以Web应用程序的语言注入并在服务器上执行,从而潜在地允许任意代码执行。让我们看看NoSQL注入与传统SQL注入有何不同,以及如何防止它。
快速入门NoSQL
即使您不使用数据库,也可能在过去几年中与云相关的流行语中听说过NoSQL。NoSQL(又称“非SQL”或“不仅是SQL”)是一个通用术语,涵盖了不使用SQL查询语言的数据库。实际上,它被用来指代作为分布式云平台和Web应用程序的后端而越来越流行的非关系数据库。NoSQL数据存储区不像关系数据库那样将数据存储在表中,而是使用其他更适合特定目的的数据模型,例如文档,图形,对象等。
MongoDB是当前最流行的NoSQL数据库产品之一,它使用类似于JSON(JavaScript对象表示法)的语法将数据存储为文档。除其他好处外,这还使开发人员可以仅使用JavaScript来构建全栈应用程序。我们将使用MongoDB查询来演示攻击者如何利用未经验证的用户输入将代码注入由NoSQL数据库支持的Web应用程序中。
为什么可以进行MongoDB注入
使用传统的SQL注入,攻击者利用不安全的用户输入处理来修改或替换应用程序发送到数据库引擎的SQL查询(或其他SQL语句)。
换句话说,SQL注入使攻击者可以在数据库中执行命令。
与关系数据库不同,NoSQL数据库不使用通用查询语言。NoSQL查询语法是特定于产品的,查询是使用应用程序的编程语言编写的:PHP,JavaScript,Python,Java等。这意味着成功的注入使攻击者不仅可以在数据库中执行命令,而且可以在应用程序本身中执行命令,这可能更加危险。
MongoDB使用二进制JSON(BSON)数据格式,并带有安全的BSON查询组装工具。查询也表示为BSON对象(即二进制数据),因此无法进行直接字符串注入。但是,MongoDB故意允许应用程序在$where
和mapReduce
操作中在服务器上运行JavaScript 。该文档是非常清楚的,如果使用此功能,开发人员应该注意防止用户提交恶意的JavaScript。换句话说,MongoDB故意包含潜在的注入向量。那么,如果开发人员不小心,被恶意用户利用怎么办?

PHP中的简单MongoDB注入
对于基本的身份验证绕过,攻击者可以尝试在字段值中输入MongoDB运算符,例如$eq
(等于),$ne
(不等于)或$gt
(大于)。这是在PHP应用程序中使用直接从表单获取参数值的方式来构建数据库查询的不安全方法:
$query = array("user" => $_POST["username"], "password" =>
$_POST["password"]);
如果此查询随后用于检查登录凭据,则攻击者可以滥用PHP的内置关联数组处理来注入始终返回true并绕过身份验证过程的MongoDB查询。这可能与发送以下POST请求一样简单:
username[$ne]=1&password[$ne]=1
PHP会将其转换为数组数组:
array("username" => array("$ne" => 1), "password" =>
array("$ne" => 1));
当作为MongoDB查询发送到用户存储时,它将查找用户名和密码不等于1的所有用户,这很可能是真实的,并且可能允许攻击者绕过身份验证。
MongoDB中的JavaScript注入
假设我们有一个易受攻击的应用程序,其中开发人员将MongoDB的$where
查询运算符与未经验证的用户输入一起使用。这使攻击者可以注入包含JavaScript代码的恶意输入。尽管攻击者无法注入完全任意的JavaScript,但仅注入使用功能有限的代码的代码,这对进行有用的攻击就足够了。
要使用$where
运算符查询MongoDB数据存储,通常会使用该find()
函数,例如:
db.collection.find( { $where: function() {
return (this.name == 'Netsparker') } } );
这将匹配名称为Netsparker的记录。易受攻击的PHP应用程序在构建查询时可能会直接插入未经过处理的用户输入,例如从变量中$userData
:
db.collection.find( { $where: function() {
return (this.name == $userData) } } );
然后,攻击者可能会注入一种利用字符串如'a'; sleep(5000)
成$userData
有5秒钟的暂停服务器如果注入成功。服务器执行的查询为:
db.collection.find( { $where: function() {
return (this.name == 'a'; sleep(5000) ) } } );
因为查询是用应用程序语言编写的,所以这只是多种可能的注入方式之一。例如,如果使用Node.js进行服务器端脚本编写,例如在流行的MEAN堆栈(MongoDB,ExpressJS,AngularJS和Node.js)中,则可以将服务器端JavaScript注入到Node.js中。
防止NoSQL注入攻击
成功的MongoDB注入或其他NoSQL注入攻击的后果可能比传统SQL注入更为严重。攻击者不仅可以从数据库中提取数据,还可以在应用程序的上下文中执行代码,例如执行拒绝服务攻击,甚至破坏管理员用户帐户并控制服务器。这种攻击尤其危险,因为NoSQL数据存储对于仅熟悉关系数据库产品的开发人员而言通常是新颖的,这增加了代码不安全的风险。
许多流行的NoSQL产品仍处于起步阶段,并且正在紧锣密鼓地开发中,因此使用最新版本始终是一个好主意。例如,众所周知,MongoDB的早期版本在许多级别上都是不安全的,并且具有严重的注入漏洞,而在较新的版本中,安全性受到了更多重视。
与Web应用程序安全性一样,防止NoSQL注入攻击的最佳方法是避免在应用程序代码中使用未经处理的用户输入,尤其是在构建数据库查询时。例如,MongoDB具有内置功能,可以在不使用JavaScript的情况下安全地构建查询。如果确实需要在查询中使用JavaScript,请遵循通常的最佳实践:验证和编码所有用户输入,应用最小特权规则,并了解您的语言以避免使用易受攻击的构造。
MongoDB如何解决SQL注入?
BSON
客户端程序在MongoDB中组装查询时,将构建BSON对象而不是字符串。因此,传统的SQL注入攻击不是问题。更多细节和一些细微差别将在下面介绍。
MongoDB将查询表示为BSON对象。通常, 客户端库提供了一个方便,无注入的过程来构建这些对象。考虑下面的C ++示例:
BSONObj my_query = BSON( "name" << a_name );
auto_ptr<DBClientCursor> cursor = c.query("tutorial.persons", my_query);
在这里,my_query
将有一个诸如{ name : “Joe” }的值。如果my_query包含特殊字符,则查询将根本不匹配任何文档。例如 ,
, :
, 和 {
, 用户无法劫持查询并将其转换为删除。
JavaScrip
您可以通过--noscripting
在命令行上传递选项或security.javascriptEnabled
在配置文件中进行设置来禁用所有服务器端JavaScript的执行 。
以下所有MongoDB操作都允许您直接在服务器上运行任意JavaScript表达式:
$where
mapReduce
在这些情况下,您必须格外小心,以防止用户提交恶意JavaScript。
幸运的是,您可以在没有JavaScript的MongoDB中表达大多数查询,对于需要JavaScript的查询,可以在单个查询中混合使用JavaScript和非JavaScript。将所有用户提供的字段直接放在BSON字段中,并将JavaScript代码传递给该$where
字段。
如果需要在$where
子句中传递用户提供的值,则可以使用该CodeWScope
机制来转义这些值。在范围文档中将用户提交的值设置为变量时,可以避免在数据库服务器上运算它们。
测试NoSQL注入
摘要
与传统的SQL数据库相比,NoSQL数据库提供了更宽松的一致性限制。通过要求更少的关系约束和一致性检查,NoSQL数据库通常提供性能和扩展优势。然而,即使这些数据库没有使用传统的SQL语法,它们仍然可能容易受到注入攻击。由于这些NoSQL注入攻击可能在过程语言中执行,而不是在声明性SQL语言中执行,因此潜在的影响比传统的SQL注入更大。
的NoSQL数据库调用都写在应用程序的编程语言,一个自定义的API调用,或格式化按照通常的习惯(如XML
,JSON
,LINQ
,等)。针对这些规范的恶意输入可能不会触发主要的应用程序清理检查。例如,过滤掉常见的HTML特殊字符(例如)< > & ;
将不会阻止针对JSON API的攻击,其中特殊字符包括/ { } :
。
现在,在应用程序中可以使用超过150个NoSQL数据库,以各种语言和关系模型提供API。每个提供不同的功能和限制。由于它们之间没有通用语言,因此示例注入代码将不适用于所有NoSQL数据库。因此,任何测试NoSQL注入攻击的人都需要熟悉语法,数据模型和基础编程语言,以便进行特定的测试。
与传统的SQL注入相比,NoSQL注入攻击可能在应用程序的不同区域中执行。在数据库引擎内执行SQL注入的地方,NoSQL变体可以在应用程序层或数据库层内执行,这取决于所使用的NoSQL API和数据模型。通常,NoSQL注入攻击将在攻击字符串被解析,评估或连接到NoSQL API调用的地方执行。
其他计时攻击可能与NoSQL数据库中缺少并发检查有关。这些不在注入测试范围内。在撰写本文时,MongoDB是使用最广泛的NoSQL数据库,因此所有示例都将具有MongoDB API。
测试方法
在MongoDB中测试NoSQL注入漏洞
MongoDB API需要BSON(二进制JSON)调用,并包括一个安全的BSON查询组装工具。但是,根据MongoDB文档- 在多个替代查询参数中允许未序列化的JSON和JavaScript表达式。允许任意JavaScript输入的最常用的API调用是$where
运算符。
由于$where
在SQL中,MongoDB 运算符通常用作简单的过滤器或检查。
db.myCollection.find( { $where: "this.credits==this.debits" } );
还可以选择对JavaScript进行评估,以允许更高级的条件。
db.myCollection.find( { $where: function() { return obj.credits - obj.debits < 0; } } );
例子1
如果攻击者能够操纵传递给$where
运算符的数据,则该攻击者可以包含任意JavaScript,以将其评估为MongoDB查询的一部分。如果用户输入不经过清理就直接传递到MongoDB查询中,则以下代码中暴露了一个示例漏洞。
db.myCollection.find( { active: true, $where: function() { return obj.credits - obj.debits < $userInput; } } );;
与测试其他类型的注入一样,不需要完全利用此漏洞来证明问题。通过注入与目标API语言相关的特殊字符并观察结果,测试人员可以确定应用程序是否正确清除了输入。例如在MongoDB中,如果未经过滤传递包含以下任何特殊字符的字符串,则会触发数据库错误。
' " \ ; { }
使用普通的SQL注入,类似的漏洞将使攻击者可以执行任意SQL命令-随意公开或处理数据。但是,由于JavaScript是一种功能全面的语言,因此,这不仅使攻击者可以操纵数据,还可以运行任意代码。例如,一个完整的利用漏洞不仅会在测试时引起错误,还可以使用特殊字符来制作有效的JavaScript。
0;var date=new Date(); do{curDate = new Date();}while(curDate-date<10000)
插入$userInput
以上示例代码中的此输入将导致执行以下JavaScript函数。此特定的攻击字符串将使整个MongoDB实例在100%CPU使用率下执行10秒钟。
function() { return obj.credits - obj.debits < 0;var date=new Date(); do{curDate = new Date();}while(curDate-date<10000); }
例子2
即使查询中使用的输入已被完全清理或参数化,也存在另一种可能触发NoSQL注入的路径。许多NoSQL实例都有自己的保留变量名,与应用程序编程语言无关。
例如,在MongoDB中,$where
语法本身是保留的查询运算符。需要完全按照所示将其传递到查询中。任何更改都会导致数据库错误。但是,由于$where
也是有效的PHP变量名称,因此攻击者有可能通过创建名为的PHP变量将代码插入查询中$where
。PHP MongoDB文档明确警告开发人员:
请确保对于所有特殊查询运算符(以$开头)都使用单引号,以使PHP不会尝试$exists
用variable的值替换$exists
。
即使查询不依赖用户输入,例如以下示例,攻击者也可以通过用恶意数据替换操作员来利用MongoDB。
db.myCollection.find( { $where: function() { return obj.credits - obj.debits < 0; } } );
将数据潜在地分配给PHP变量的一种方法是通过HTTP参数污染(请参阅:测试HTTP参数污染)。通过创建一个$where
通过参数污染命名的变量,可以触发MongoDB错误,指示该查询不再有效。$where
字符串$where
本身以外的任何其他值都应足以证明漏洞。攻击者可以通过插入以下代码来开发完整的利用方法:
$where: function() { //arbitrary JavaScript here }
from owasp