最近发生一件事, 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的防范方式。其他地方提到的资料基本上大同小异。
根据上面两份资料的内容,以及其他在网上找到的内容:
No SQL不代表安全。由于No SQL使用的查询语言很多是过程式语言而非声明式语言,No SQL注入的危害甚至比传统的SQL数据库更大。
不要草率地通过拼接用户输入的方式来生成查询语句。这个就不需要举例吧,了解MongoDB查询语言的人,应该可以想象到会是什么结果。
好消息:MongoDB会在driver中将查询字符串编码成BSON格式。类似于
,:{
这样的奇奇怪怪的符号会导致BSON构造出错。这样cracker在注入时需要花费更多的心思了。坏消息:
$
和.
不在上述符号之列。又一个好消息:Mongoose宣称,为了提高安全性,它会根据Schema来强制转型输入的内容,因此像
$xxx
这样的变量会被转化成预定义的类型。如果你还是放心不下,参照 http://stackoverflow.com/questions/15917400/how-dangerous-is-a-mongo-query-which-is-fed-directly-from-a-url-query-string 里面的回答,自己写验证函数。最后是坏消息。在下列四个函数中,你可以直接传递字符串作为参数。记住它们的样子,见到它们可以高呼“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参数,来绕过逻辑判断。
所以说,搞了半天,最后还是回到一个老问题上了,永远不要相信用户的一切输入!
总结
- No SQL也会有SQL injection。
- 最好不要拼接查询语句,因为漏洞什么的,你永远也想不到会从哪里冒出来。
- 注意MongoDB查询语句中的危险分子。
- 永远不要相信用户的输入。无论什么输入,都要在服务端程序中过一下,哪怕是转化成字符串这样的操作也好。