你點下「隨機」按鈕,程式在瞬間給了你一個數字。這個過程看起來簡單,背後卻涉及一個電腦科學的根本悖論:電腦是確定性的機器——相同的輸入,永遠產生相同的輸出。那它怎麼可能「真正隨機」?從幸運轉盤到密碼產生器,不同工具對「隨機」的要求完全不同,而這個差異,在某些場景下會決定你的帳戶是否安全。
1. 電腦為什麼不能「真正隨機」?
傳統電腦是確定性圖靈機的實作:所有操作都依照明確的規則執行,相同的初始狀態必然導致相同的後續狀態。這表示任何純粹在電腦內部運行的演算法,其輸出都是由初始值(稱為「種子」,seed)完全決定的。
如果你知道種子,你就能預測所有後續的「隨機」數字。這類演算法生成的數字序列,在統計上看起來像隨機的(通過各種隨機性測試),但本質上是可預測的、週期性的偽隨機序列。這就是為什麼電腦科學領域把它們稱為「偽隨機數生成器」(Pseudo-Random Number Generator,PRNG)。
偽隨機並不是壞事——對於模擬、遊戲、統計抽樣等大多數應用場景,PRNG 完全夠用。問題只在於需要「不可預測性」的場景(密碼學)——這時偽隨機就是一個嚴重的安全漏洞。
2. 偽隨機數生成器(PRNG)的原理
2.1 線性同餘生成器(LCG)
最古老也最簡單的 PRNG 之一。公式如下:
X(n+1) = (a × X(n) + c) mod m
其中 a(乘數)、c(增量)、m(模數)是預先設定的常數,X(0) 是種子。每次呼叫就根據前一個值計算出下一個值。
LCG 速度極快、記憶體占用極少,但品質有限:週期(重複之前循環的長度)有限,且在多維空間中的分佈會呈現規律性的超平面結構,被統計測試容易檢測出來。早期的 ANSI C rand() 函式就是 LCG,現在已經很少單獨使用。
2.2 梅森旋轉演算法(Mersenne Twister)
1998 年由松本眞和西村拓士提出,MT19937 是目前應用最廣泛的 PRNG 之一。它的週期長達 2^19937 − 1(一個天文數字般的大數),並通過了幾乎所有統計隨機性測試。Python 的 random 模組、Ruby、PHP 的 rand() 等語言的預設隨機函式,都使用梅森旋轉。
然而,梅森旋轉也有一個嚴重的弱點:它的內部狀態是 624 個 32 位整數。如果攻擊者觀察到連續 624 個輸出值,就能完全重建內部狀態,從而預測所有後續輸出。這讓它完全不適合任何需要安全性的應用。
2.3 Xorshift 與現代高品質 PRNG
近年出現了一批更現代的 PRNG,如 Xorshift128+、PCG(Permuted Congruential Generator)、xoshiro256**,它們在速度、統計品質和週期上都有優秀表現,但同樣不適合密碼學應用。
| 演算法 | 週期 | 速度 | 統計品質 | 密碼學安全? |
|---|---|---|---|---|
| LCG(線性同餘) | 中等 | 極快 | 一般 | 否 |
| 梅森旋轉 MT19937 | 2^19937−1 | 快 | 優良 | 否 |
| PCG / xoshiro | 2^128 以上 | 極快 | 優良 | 否 |
| ChaCha20(CSPRNG) | 不適用 | 快 | 優良 | 是 |
| 硬體亂數(TRNG) | 不適用 | 慢 | 真隨機 | 是 |
3. 真隨機:從物理世界取得不可預測性
要突破確定性電腦的限制,就必須引入來自外部世界的熵(Entropy)——真正不可預測的物理事件。作業系統和硬體透過多種方式收集這些熵:
- 使用者行為:滑鼠移動軌跡、鍵盤按鍵時機——人類的動作具有極高的不可預測性
- 硬體中斷時機:磁碟讀寫、網路封包到達的精確時間點,受到電磁雜訊等物理因素的影響
- CPU 時序抖動:處理器的微小時間差異(jitter),受溫度、電壓波動等物理因素影響
- 專用硬體亂數生成器(HRNG/TRNG):利用量子效應(如熱雜訊、放射性衰變、光子行為)直接產生真隨機位元的硬體設備
Linux 的 /dev/urandom 和 /dev/random 就是作業系統維護的熵池介面,收集上述各種硬體事件,為密碼學應用提供高品質的隨機種子。Intel 的 RDRAND 指令和 AMD 的對應實作,則直接在 CPU 層級提供硬體亂數。
/dev/random vs /dev/urandom:有什麼差別?/dev/random 在熵池不足時會「阻塞」等待更多熵積累;/dev/urandom 在熵不足時會繼續使用 CSPRNG 延伸輸出,不阻塞。現代系統上,初始化後的 /dev/urandom 對幾乎所有應用已足夠安全,不需要使用 /dev/random 的嚴格模式。
4. 密碼學安全亂數生成器(CSPRNG)
密碼學安全亂數生成器(Cryptographically Secure Pseudo-Random Number Generator,CSPRNG)結合了 PRNG 的速度與熵源的不可預測性。它必須滿足兩個核心要求:
- 下一個位元不可預測性(Next-bit unpredictability):即使知道所有已輸出的位元,也無法以優於 50% 的機率預測下一個位元。
- 狀態妥協後向安全性(Backward security / State compromise resistance):即使攻擊者在某個時刻取得了 CSPRNG 的內部狀態,也無法回溯推算出過去已生成的輸出。
常見的 CSPRNG 包括:
- ChaCha20-based CSPRNG:Linux kernel 4.8+ 的
/dev/urandom實作基礎,也被 OpenBSD 的arc4random採用 - Fortuna:Windows 的 BCryptGenRandom 使用的演算法基礎之一
- Hash_DRBG / HMAC_DRBG:NIST 標準化的 CSPRNG,廣泛用於各類安全程式庫
5. 為什麼 Math.random() 絕不能用於密碼?
這是最常見也最危險的誤解之一。幾乎所有語言內建的「基礎隨機函式」——JavaScript 的 Math.random()、Python 的 random.random()、PHP 的 rand()——都使用 PRNG 而非 CSPRNG。
以 JavaScript 的 Math.random() 為例,各瀏覽器的實作基本上都使用 xorshift128+ 演算法。研究人員在 2016 年的論文中展示:只需觀察 3 個連續的 Math.random() 浮點數輸出,就可以完全重建內部狀態,從而預測所有後續的「隨機」數字。
如果你用 Math.random() 產生密碼,攻擊者只需要知道你的種子(或觀察到足夠多的輸出),就能枚舉出所有可能的密碼,而不是對抗密碼的全部字元空間。正確的做法是使用專為密碼學設計的 API:
- JavaScript/Web:
window.crypto.getRandomValues() - Node.js:
crypto.randomBytes() - Python:
secrets模組(Python 3.6+) - PHP:
random_bytes()、random_int() - Java:
java.security.SecureRandom
許多人在學習時會寫
random.seed(42) 讓結果可重現——這在教學和測試中是好的實踐。但這也直接說明了 PRNG 的本質:種子一旦固定,所有輸出都是確定的。如果你的種子是可猜測的(如當前時間戳),攻擊者只需嘗試有限的種子值,就能還原你的所有「隨機」輸出。
6. 不同應用場景的亂數需求
6.1 遊戲與娛樂:PRNG 已足夠
骰子模擬、幸運轉盤、抽籤工具、卡牌遊戲——這類應用的核心需求是:分佈均勻、速度快、用戶感受到「公平」。梅森旋轉或現代 PRNG 完全滿足需求。即使有人能預測輸出,後果最多是「知道轉盤下一次會停在哪裡」,不構成安全威脅。
6.2 統計模擬:PRNG + 種子控制
蒙地卡羅模擬、統計抽樣、機器學習的資料分割——需要高品質的統計分佈(通過 Diehard 等測試),同時希望結果可重現以便驗證。固定種子的 PRNG 是標準做法。
6.3 密碼學與安全:必須使用 CSPRNG
密碼產生、加密金鑰、Token、Session ID、CSRF Token、一次性密碼(OTP)——這些場景的安全性完全依賴亂數的不可預測性。任何 PRNG 都是不可接受的,必須使用作業系統提供的 CSPRNG 介面。
6.4 科學實驗:TRNG 或高品質 PRNG
真正需要「真隨機」的場景(如量子力學實驗、隨機對照試驗的參與者分組)會使用硬體真隨機數生成器,或使用 random.org 等基於物理雜訊的線上服務。
| 應用場景 | 推薦類型 | 範例 API | 關鍵需求 |
|---|---|---|---|
| 遊戲/轉盤/骰子 | PRNG | Math.random() | 均勻分佈、速度 |
| 統計模擬 | PRNG(固定種子) | random.seed(n) | 可重現性、統計品質 |
| 密碼/金鑰/Token | CSPRNG | crypto.getRandomValues() | 不可預測性 |
| 科學實驗 | TRNG | RDRAND / random.org | 真實熵源 |
7. 隨機性的哲學與現實意義
從更深的層次看,「真隨機」在物理學上也是一個複雜的問題。在量子力學中,粒子的某些測量結果(如光子通過偏振片的路徑)被認為是本質上不確定的——這是目前物理學所知最接近「真隨機」的現象。相比之下,古典物理中的所謂「隨機」(如擲骰子)理論上是完全可預測的:如果你知道骰子的精確初速度、空氣阻力、桌面彈性係數,就能計算出結果。
但對於工程實踐而言,這個哲學層面的討論幾乎無關緊要。實際上重要的是:攻擊者能否在可接受的計算時間內預測輸出? 現代 CSPRNG 的設計目標就是讓這個問題的答案為「否」,而不是追求形而上學意義上的「真隨機」。
8. 小結
電腦產生亂數的機制,從表面的「點一下就出現數字」背後,隱藏著豐富的技術層次:
- PRNG(偽隨機):確定性演算法,速度快、統計品質好,但本質可預測——適合遊戲、模擬,不適合密碼學
- TRNG(真隨機):從物理世界收集熵,真正不可預測——速度慢,用於提供種子或特殊需求
- CSPRNG(密碼學安全亂數):結合 PRNG 的速度與熵源的不可預測性,滿足密碼學安全要求——密碼、金鑰、Token 的唯一正確選擇
下次你使用密碼產生器或看到「隨機」功能時,可以問一個問題:這個場景需要的是「看起來隨機」,還是「真的無法預測」?這個區別,可能就是你資料安全的邊界所在。