在密碼學和許多安全相關的功能中,「隨機性」是不可或缺的基石。如果應用程式在需要不可預測性的情境下(如生成 Session ID、密碼重設權杖),卻使用了可被預測的「隨機」數,就會產生嚴重的安全漏洞。
什麼是不安全的隨機性?
不安全的隨機性 (Insecure Randomness) 指的是,應用程式使用了品質不佳的偽隨機數生成器 (Pseudo-Random Number Generator, PRNG)。這類生成器產生的數列雖然看起來隨機,但其內部是基於一個可預測的「種子 (Seed)」透過特定演算法計算出來的。如果攻擊者能夠知道或猜測到這個種子,他們就能夠重現整個隨機數序列。
在安全性要求高的場景,必須使用密碼學安全偽隨機數生成器 (Cryptographically Secure Pseudo-Random Number Generator, CSPRNG)。CSPRNG 的設計確保了其輸出結果在計算上是不可預測的。
攻擊原理與範例
情境:可預測的密碼重設權杖
- 使用者請求重設密碼:小明忘記了密碼,於是在網站上點擊「忘記密碼」。
- 生成重設權杖:網站後端程式為了驗證小明的身份,需要生成一個隨機的 URL 權杖,並寄到他的信箱。但工程師使用了
Math.random()
或一個以當前時間(如毫秒)為種子的 PRNG 來生成這個權杖。 - 攻擊者預測權杖:攻擊者此時也對同一個網站發起「忘記密碼」的請求,目標是自己的帳戶。他觀察到自己收到的權杖與請求時間有高度關聯。
- 計算受害者權杖:由於攻擊者大致知道小明是在什麼時候請求重設密碼的,他可以根據自己收到的權杖和時間,反推出小明收到的權杖可能的值,並進行嘗試。
- 成功重設密碼:一旦猜中,攻擊者就能用小明的身份設定新密碼,並接管其帳戶。
風險與影響
- Session 劫持:如果 Session ID 是可預測的,攻擊者可以猜測到合法使用者的 Session ID 並冒用其身份。
- 繞過身份驗證:可預測的密碼重設權杖、一次性密碼 (OTP) 都會導致身份驗證機制失效。
- 加密金鑰破解:如果隨機數被用來生成加密金鑰,可預測的隨機性會大幅降低金鑰的強度,使其容易被破解。
防禦與預防措施
永遠使用密碼學安全的隨機數生成器 (CSPRNG)
這是防禦此漏洞的唯一準則。開發者不應自行嘗試發明隨機數演算法。
- 選擇正確的工具:使用您所用程式語言或框架中專為安全目的設計的函式庫。
- Python:
secrets
模組或os.urandom()
。 - Java:
java.security.SecureRandom
。 - Node.js:
crypto.randomBytes()
。 - PHP:
random_bytes()
或random_int()
。
- Python:
- 確保足夠的熵 (Entropy):CSPRNG 通常會從作業系統收集足夠的「熵」(硬體噪音、系統事件等不可預測的輸入)來確保其隨機性。
- 確保足夠的長度:生成的權杖或金鑰應有足夠的長度,以防禦暴力破解攻擊。例如,一個 Session ID 至少應為 128 位元(16 位元組)。
錯誤的程式碼範例
下面的 Python 程式碼試圖從 0 到 9999 之間選擇一個數字作為密碼值。
1 | # 錯誤範例 |
問題分析:
- 範圍過小:
xrange(0, 10000)
(應為range
)只產生最多 4 位數的數字。這個密碼空間太小,可以被輕易地暴力破解。 - 複雜度不足:純數字密碼,沒有字母或特殊字元,安全性極低。
- 可能使用不安全的 PRNG:
random
模組不適用於密碼學場景。雖然範例中用了SystemRandom
的別名sr
,但random.choice
本身不是生成安全權杖的最佳實踐。
正確的做法應該是使用 secrets
模組生成一個足夠長且包含多種字元的隨機字串。
說些什麼吧!