1、攻擊測試
先拿 Laravel 試試~~ 我拉了一個最新版本 v8.5.15
可以看到這個請求竟然花了27秒!
再來試試傳入正常的資料是怎樣的:
只用了200毫秒,可以得出結論這個攻擊在 Laravel 裡是奏效的~ 當然也不能怪 Laravel,這個鍋 PHP 背,為什麼呢?(如果伺服器突然 CPU 飆升站長大人不要打我)
2、HashTable
之前一直知道 PHP 的陣列其實就是 HashTable,但沒有深入去研究,直到昨天看見鳥哥的一篇文章 PHP陣列的Hash衝突例項 拿裡面的程式碼去跑了跑竟然在今天依然有效~ 我當時也是懵的。。我的本地 PHP版本:
攻擊原理(簡單介紹)
更詳細的介紹可以看看這篇帖子:什麼是雜湊洪水攻擊(Hash-Flooding Attack)?
首先任何 Hash Function 都會有雜湊衝突的問題,所以一般解決衝突的辦法有以下三種
- 開放定址方法
- 拉鍊法(到達一定的長度之後可以轉換為紅黑樹提高效能,Java 目前就是這樣做的,PHP 目前並沒有)
- 重雜湊法
PHP 採用的就是【拉鍊法】,將衝突的 Bucket 串成連結串列,在取資料時透過雜湊函式定位到對應的 Bucket 連結串列然後遍歷連結串列,逐個對比 Key 值直到找出目標元素。
而這段程式碼就是將 PHP 的 HashTable 退化成了連結串列,使每次插入的平均時間度變成了 O(n)
那為什麼上面那段程式碼會奏效呢?引用自 PHP陣列的Hash衝突例項:
這樣在每次插入的時候PHP都需要遍歷一遍這個連結串列, 大家可以想象, 第一次插入, 需要遍歷0個元素, 第二次是1個, 第三次是3個, 第65536個是65535個, 那麼總共就需要65534*65535/2=2147385345次遍歷….
在PHP中,如果鍵值是數字,那麼Hash的時候就是數字本身, 一般的時候都是 index & tableMask,而tableMask是用來保證數字索引不會超出陣列可容納的元素個數值, 也就是陣列個數-1。
PHP的Hashtable的大小都是2的指數,比如如果你存入10個元素的陣列,那麼陣列實際大小是16,如果存入20個,則實際大小為32,而63個話,實際大小為64。當你的存入的元素個數大於了陣列目前的最多元素個數的時候,PHP會對這個陣列進行擴容,並且從新Hash。
現在,我們假設要存入64個元素(中間可能會經過擴容,但是我們只需要知道,最後的陣列大小是64,並且對應的tableMask為63:0111111),那麼如果第一次我們存入的元素的鍵值為0,則hash後的值為0,第二次我們存入64,hash(1000000 & 0111111)的值也為0,第三次我們用128,第四次用192… 就可以使得底層的 PHP 陣列把所有的元素都 Hash 到0號 bucket 上, 從而使得 Hash 表退化成連結串列了.
當然, 如果鍵值是字串的話, 就稍微比較麻煩一些了, 但是 PHP 的 Hash 演算法是開源的, 已知的, 所以有心人也可以做到…
怎麼避免
- PHP 在5.4版本加入了一個配置引數:max_input_vars,作用為:
- 加入隨機 salt(目前來看PHP並沒有加入?有大佬解一下惑嗎~)
為此我特意用 PHP 仿寫了一個陣列結構,裡面有加入 salt 元素,感興趣的朋友可以看看: PHP-HashTable
具體原理還是這篇第一個高贊回答:www.zhihu.com/question/286529973
3、為什麼 Laravel 會中招
在上面可以看到 max_input_vars 似乎只對 $_GET $_POST $_COOKIE 這三個超全域性變數生效。
在今天前後端分離的開發模式下大部分傳輸型別頭都採用 Content-Type : Application/Json 來進行資料的傳輸,而 $_POST 只能獲取 Content-Type 為 application/x-www-form-urlencoded 或者 multipart/form-data 的資料 php://input vs $_POST,這個時候想要獲取 POST 資料你必須使用 file_get_contents(“php://input”) 來獲取原始資料,獲取原始資料之後再使用 json_decode($data, true) 轉換為陣列,只要經過這步就已經中招了~~
而 Laravel 為了讓大家開箱即用對於任何型別資料都已經預先處理過了,而正是這些預先處理使得 Laravel 會被 100% 命中,下面看看 Laravel 對於 Content-Type: Application/Json 的請求處理
最後
目前來看並不是只有 Laravel 有這樣的問題,只要用了 PHP 就可能有這個風險~~
最後看下我測自己伺服器的情況
可以發現只需極少的成本就能打垮伺服器~
本作品採用《CC 協議》,轉載必須註明作者和本文連結