Insecure Randomness
Abstract
標准的偽隨機數生成器不能抵擋各種加密攻擊。
Explanation
在對安全性要求較高的環境中,使用一個能產生可預測數值的函數作為隨機數據源,會產生 Insecure
Randomness 錯誤。 電腦是一種具有確定性的機器,因此不可能產生真正的隨機性。 偽隨機數生成器
(PRNG) 近似於隨機算法,始於一個能計算后續數值的種子。 PRNG 包括兩種類型: 統計學的 PRNG 和密
碼學的 PRNG。 統計學的 PRNG 可提供有用的統計資料,但其輸出結果很容易預測,因此數據流容易復制。
若安全性取決於生成數值的不可預測性,則此類型不適用。 密碼學的 PRNG 通過可產生較難預測的輸出結果
來應對這一問題。 為了使加密數值更為安全,必須使攻擊者根本無法、或極不可能將它與真實的隨機數加以
區分。 通常情況下,如果並未聲明 PRNG 算法帶有加密保護,那么它有可能就是一個統計學的 PRNG,不應
在對安全性要求較高的環境中使用。
示例: 下面的代碼可利用統計學的 PRNG 為購買產品后仍在有效期內
的收據創建一個 URL。
String GenerateReceiptURL(String baseUrl) { Random ranGen = new Random(); ranGen.setSeed((new Date()).getTime()); return(baseUrl + Gen.nextInt(400000000) + ".html"); }
這段代碼使用 Random.Next() 函數為由其產生的收據頁面生成“唯一”的標識符。 因為
Random.nextInt() 是一個統計學 PRNG,攻擊者很容易就能猜到由它生成的字符串。 盡管收據系統的底
層設計也存在錯誤,但如果使用了一個不生成可預測收據標識符的隨機數生成器(如密碼學的 PRNG),會
更安全一些。
Recommendation
當不可預測性至關重要時,如大多數對安全性要求較高的環境都采用隨機性,這時可以使用密碼學的 PRNG
。 不管選擇了哪一種 PRNG,都要始終使用帶有充足熵的數值作為該算法的種子。 (諸如當前時間之類的數
值只提供很小的熵,因此不應該使用。) Java 語言在 java.security.SecureRandom 中提供了一個加
密 PRNG。 就像 java.security 中其他以算法為基礎的類那樣,SecureRandom 提供了與某個特定算法
集合相關的包,該包可以獨立實現。 當使用 SecureRandom.getInstance() 請求一個 SecureRandom
實例時,您可以申請實現某個特定的算法。 如果算法可行,那么您可以將它作為 SecureRandom 的對象使
用。 如果算法不可行,或者您沒有為算法明確特定的實現方法,那么會由系統為您選擇 SecureRandom 的
實現方法。 Sun 在名為 SHA1PRNG 的 Java 版本中提供了一種單獨實現 SecureRandom 的方式,Sun 將其
描述為計算: “SHA-1 可以計算一個真實的隨機種子參數的散列值,同時,該種子參數帶有一個 64 比特的計
算器,會在每一次操作后加 1。 在 160 比特的 SHA-1 輸出中,只能使用 64 比特的輸出 [1]。” 然而,文檔中
有關 Sun 的 SHA1PRNG 算法實現細節的相關記錄很少,人們無法了解算法實現中使用的熵的來源,因此也並
不清楚輸出中到底存在多少真實的隨機數值。 盡管有關 Sun 的實現方法網絡上有各種各樣的猜測,但是有一
點無庸置疑,即算法具有很強的加密性,可以在對安全性極為敏感的各種內容中安全地使用。
解決辦法:
采用更加安全的SecureRandom來替換Random。
