MongoDB, no SQL injection?

最近发生一件事, Ruby-China Mongodb注入可导致盗用管理员(他人)身份发帖引起了我的兴趣。

具体内容可以移步到链接去看。随便给出对应的pr地址:https://github.com/ruby-china/ruby-china/commit/ff19cc1fb6d9ea8b0109d7f62741f6652009ff3d

于是我搜索了相关资料,以搬运工的身份写下这篇文章。

MongoDB, no SQL injection?

有人的地方就有江湖,有DB的地方就有injection。SQL数据库如此,No SQL数据库亦是如此。考虑到No SQL数据库用的不是SQL,这里使用SQL injection是否有点不太恰当?不过总不能说是No SQL injection吧XD。

言归正传,OWASP上面有一篇文章, https://www.owasp.org/index.php/Testing_for_NoSQL_injection, 讲到MongoDB的注入问题。另外,MongoDB官方文档FAQ中也提到对于SQL injection的防范方式。其他地方提到的资料基本上大同小异。

根据上面两份资料的内容,以及其他在网上找到的内容:

  1. No SQL不代表安全。由于No SQL使用的查询语言很多是过程式语言而非声明式语言,No SQL注入的危害甚至比传统的SQL数据库更大。

  2. 不要草率地通过拼接用户输入的方式来生成查询语句。这个就不需要举例吧,了解MongoDB查询语言的人,应该可以想象到会是什么结果。

  3. 好消息:MongoDB会在driver中将查询字符串编码成BSON格式。类似于,:{这样的奇奇怪怪的符号会导致BSON构造出错。这样cracker在注入时需要花费更多的心思了。

  4. 坏消息:$.不在上述符号之列。

  5. 又一个好消息:Mongoose宣称,为了提高安全性,它会根据Schema来强制转型输入的内容,因此像$xxx这样的变量会被转化成预定义的类型。如果你还是放心不下,参照 http://stackoverflow.com/questions/15917400/how-dangerous-is-a-mongo-query-which-is-fed-directly-from-a-url-query-string 里面的回答,自己写验证函数。

  6. 最后是坏消息。在下列四个函数中,你可以直接传递字符串作为参数。记住它们的样子,见到它们可以高呼“Eval is evil!”

具体是干什么的,请参考对应文档。

注意$where,这个跟MongoDB ORM(姑且这么称呼)中提供的where调用不一样。这个$where可以接受任意查询表达式,并且返回其结果,相当于特殊的eval。其中Mongoose也特别提到了它:

  • ####NOTE:
  • Only use $where when you have a condition that cannot be met using other MongoDB operators like $lt.
  • Be sure to read about all of its caveats before using.

https://github.com/LearnBoost/mongoose/blob/master/lib/query.js

其实,Ruby China事件其中的主要问题跟MongoDB注入无关……

前面说了一大堆,关于MongoDB注入的问题,但是Ruby China事件的主要问题,跟MongoDB无关……(XD请各位看官先吸口气)

好了,主要的问题是:param的值一定是字符串么?

回到Ruby China的pr上来,这个pr主要是新增

token = params[:token] || oauth_token
+
+      # 防 mongodb 注入
+      token = token.to_s

@current_user ||= User.where(private_token: token).first

显然,token的值不一定是字符串!那么,这个引发了血案的token原本是什么值呢?

我特意用Rails测试了一下:
(注意,因为Rails是通过Rack来解析HTTP参数的,而基本上Ruby Web框架也都是使用Rack,所以下面的结果应该适用于其他Ruby框架)

...
p params[:page]
...

http://localhost:3000/page[$gt]=1
输出{"$gt"=>"1"}

现在大家知道传给User.where的token到底是什么值吧!

继续测试:
http://localhost:3000/page=[1,2]
输出[1,2],嗯…

继续继续:
http://localhost:3000/page=1&page=2
输出2

限于时间和精力,我只测试了Rails的情况。不过根据那篇乌云文章所言,PHP和Node对于HTTP参数的处理方式也跟Rails差不多。

其实,这种攻击方式并不新鲜。它甚至有一个学名,叫做HTTP Parameter Pollution,简称HPP。原理就是利用不同的服务器和服务端框架对HTTP参数的处理方式不同,提交奇奇怪怪的HTTP参数,来绕过逻辑判断。

所以说,搞了半天,最后还是回到一个老问题上了,永远不要相信用户的一切输入!

总结

  1. No SQL也会有SQL injection。
  2. 最好不要拼接查询语句,因为漏洞什么的,你永远也想不到会从哪里冒出来。
  3. 注意MongoDB查询语句中的危险分子。
  4. 永远不要相信用户的输入。无论什么输入,都要在服务端程序中过一下,哪怕是转化成字符串这样的操作也好。
    原文作者:spacewander
    原文地址: https://segmentfault.com/a/1190000002428105
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞