您好,登錄后才能下訂單哦!
這期內容當中小編將會給大家帶來有關如何理解PHP中的隨機數安全問題,文章內容豐富且以專業的角度為大家分析和敘述,閱讀完這篇文章希望大家可以有所收獲。
1、引言
西湖論劍杯線上預選賽線上賭場一題,明文攻擊出來的hint中給了/flag/seed.txt以及一個字符串code,這里需要稍微腦洞一點想到seed是指隨機數種子,以及Web頁面上的code值是每小時更換的"隨機數":
我們利用phpmtseed工具(C編寫,速度很快)可以根據隨機數碰撞出隨機數的種子,從而獲取flag:
以下是原理分析:
2、隨機數的安全缺陷
隨機數廣泛應用于生成驗證碼、Token、密鑰等場景中,分為真隨機數和偽隨機數。我們通過算法(常用線性同余)和種子(常用時鐘)得到的隨機數屬于偽隨機數:當知道種子或已產生的隨機數時,隨機數序列是可以被預測的。
可以看到PHP Manual其實提示了生成隨機數用于加密是不安全的,但是這個Caution不知為何只存在于英文版的PHP Manual中,中文版被遺漏了...這可能也是很多國內的開發應用出現過此缺陷的一個原因。
PHP中生成隨機數的函數有rand()和mtrand(),它們分別對應srand()和mtstrand()兩個用于播種隨機數種子的函數。我們建立rand.php進行測試:
<?php
mt_srand(2333);
srand(2333);
echo "seed=2333,rand()產生的隨機數序列:\n";
for($i=1;$i<=3;$i++){
echo rand()."\n";
}
echo "seed=2333,mt_rand()產生的隨機數序列:\n";
for($i=1;$i<=3;$i++){
echo mt_rand()."\n";
}
?>
執行:
可以看出當隨機數種子相同時,不管是rand()還是mtrand()產生的隨機數序列都是相同的,如果seed泄露則會導致隨機數序列的泄露。當種子值為固定如mtsrand(1000)時,隨機數形同虛設;而使用動態種子也未必安全,如:
//seed值較小,直接遍歷爆破
mt_srand(mt_rand(0,1000));
//用公開的time()作為種子,和靜態種子一樣危險
mt_srand(time());
//破解時要注意服務器時間可能存在偏差,需要設定一個較小的范圍
自PHP 4.2.0 起,隨機數發生器會自動完成播種,不再需要手工調用srand()或mt_srand(),但是這樣仍舊不安全,我們分別對兩個函數進行討論
3、rand()
rand()在產生隨機數時不會自動調用srand(),產生的隨機數序列可以通過這個式子預測:
state[i] = state[i-3] + state[i-31]
所以我們可以收集rand()生成的32位以上的隨機序列,以預測后面的隨機序列。
詳細參考:Cracking-Php-Rand
(http://www.sjoerdlangkemper.nl/2016/02/11/cracking-php-rand/)
并且在某些平臺下rand()最大值為32767,非常容易遭到爆破。
4、mt_rand()
根據PHP Manual,mtrand()產生隨機數值的平均速度比libc提供的rand()快四倍,rand() 函數默認使用 libc 隨機數發生器,mtrand() 函數是非正式用來替換它的。
mtrand()函數的安全缺陷主要出現在,所謂"自動播種"其實是PHP在同一個請求進程中只會進行一次播種,也就是說即使多次調用mtrand()函數,也只會根據第一次播種的種子生成隨機數。這一結論的證明可以通過mt_rand()的源碼分析或寫個小腳本測試來完成,不再展開,函數的核心實現代碼是這一部分:
PHPAPI void php_mt_srand(uint32_t seed)
{
/* Seed the generator with a simple uint32 */
php_mt_initialize(seed, BG(state));
php_mt_reload();
/* Seed only once */
BG(mt_rand_is_seeded) = 1;
}
/* }}} */
/* {{{ php_mt_rand
*/
PHPAPI uint32_t php_mt_rand(void)
{
/* Pull a 32-bit integer from the generator state
Every other access function simply transforms the numbers extracted here */
register uint32_t s1;
if (UNEXPECTED(!BG(mt_rand_is_seeded))) {
php_mt_srand(GENERATE_SEED());
}
if (BG(left) == 0) {
php_mt_reload();
}
--BG(left);
s1 = *BG(next)++;
s1 ^= (s1 >> 11);
s1 ^= (s1 << 7) & 0x9d2c5680U;
s1 ^= (s1 << 15) & 0xefc60000U;
return ( s1 ^ (s1 >> 18) );
}
由于根據種子生成隨機數序列的計算并不可逆,有效的破解方法應該是窮舉種子并生成隨機數序列,與已知的隨機數(序列)作比較,這也是文章開頭提到的phpmtseed工具的實現邏輯。
5、安全建議
涉及到加密/權限/CSRF Token等敏感操作時:
不要使用時間函數作為種子或直接作為隨機數:time()/microtime()
不要直接使用rand()``mt_rand()這樣的弱偽隨機數生成器
隨機數要足夠長以防御暴力破解
上述就是小編為大家分享的如何理解PHP中的隨機數安全問題了,如果剛好有類似的疑惑,不妨參照上述分析進行理解。如果想知道更多相關知識,歡迎關注億速云行業資訊頻道。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。