最近發生一件事, 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查詢語句中的危險分子。
- 永遠不要相信使用者的輸入。無論什麼輸入,都要在服務端程式中過一下,哪怕是轉化成字串這樣的操作也好。