PHP 陣列的雜湊碰撞攻擊

lidongyoo發表於2021-04-08

1、攻擊測試

先拿 Laravel 試試~~ 我拉了一個最新版本 v8.5.15

HashTable 之對 PHP陣列的go

HashTable 之對 PHP陣列的go
可以看到這個請求竟然花了27秒!
再來試試傳入正常的資料是怎樣的:

HashTable 之對 PHP陣列的go

HashTable 之對 PHP陣列的go
只用了200毫秒,可以得出結論這個攻擊在 Laravel 裡是奏效的~ 當然也不能怪 Laravel,這個鍋 PHP 背,為什麼呢?

2、HashTable

之前一直知道 PHP 的陣列其實就是 HashTable,但沒有深入去研究,直到昨天看見鳥哥的一篇文章 PHP陣列的Hash衝突例項 拿裡面的程式碼去跑了跑竟然在今天依然有效~ 我當時也是懵的。。我的本地 PHP版本:

HashTable 之對 PHP陣列的go

攻擊原理(簡單介紹)

更詳細的介紹可以看看這篇帖子:什麼是雜湊洪水攻擊(Hash-Flooding Attack)?
首先任何 Hash Function 都會有雜湊衝突的問題,所以一般解決衝突的辦法有以下三種

  • 開放定址方法
  • 拉鍊法
  • 重雜湊法

PHP 採用的就是【拉鍊法】,將衝突的 Bucket 串成連結串列,在取資料時通過雜湊函式定位到對應的 Bucket 連結串列然後遍歷連結串列,逐個對比 Key 值直到找出目標元素。
而這段程式碼就是將 PHP 的 HashTable 退化成了連結串列,使每次插入的平均時間度變成了 O(n²)
那為什麼上面那段程式碼會奏效呢?引用自 PHP陣列的Hash衝突例項

在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,作用為:
    HashTable 之對 PHP陣列的go
  • 加入隨機 salt(目前來看PHP並沒有加入?有大佬解一下惑嗎~)
    為此我特意用 PHP 仿寫了一個陣列結構,裡面有加入 salt 元素 PHP-HashTable
    具體原理還是這篇第一個高贊回答:www.zhihu.com/question/286529973

3、為什麼 Laravel 會中招

  • 在上面可以看到 max_input_time 似乎只對 $_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 對於 Content-Type: Application/Json 的請求處理

    • PHP 陣列的雜湊碰撞攻擊

    • PHP 陣列的雜湊碰撞攻擊

    • PHP 陣列的雜湊碰撞攻擊

    • PHP 陣列的雜湊碰撞攻擊

    • PHP 陣列的雜湊碰撞攻擊
      PHP 陣列的雜湊碰撞攻擊

最後

目前來看並不是這樣 Laravel 有這樣的問題,只要用了 PHP 就可能有這個風險,但是 Laravel 是 100% 命中~~
最後看下我測自己伺服器的情況

PHP 陣列的雜湊碰撞攻擊

PHP 陣列的雜湊碰撞攻擊

PHP 陣列的雜湊碰撞攻擊

可以發現只需極少的成本就能打垮伺服器~

本作品採用《CC 協議》,轉載必須註明作者和本文連結

相關文章