電腦怎麼產生亂數?偽隨機、真隨機與密碼學安全亂數完整解析

你點下「隨機」按鈕,程式在瞬間給了你一個數字。這個過程看起來簡單,背後卻涉及一個電腦科學的根本悖論:電腦是確定性的機器——相同的輸入,永遠產生相同的輸出。那它怎麼可能「真正隨機」?從幸運轉盤到密碼產生器,不同工具對「隨機」的要求完全不同,而這個差異,在某些場景下會決定你的帳戶是否安全。

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(線性同餘)中等極快一般
梅森旋轉 MT199372^19937−1優良
PCG / xoshiro2^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關鍵需求
遊戲/轉盤/骰子PRNGMath.random()均勻分佈、速度
統計模擬PRNG(固定種子)random.seed(n)可重現性、統計品質
密碼/金鑰/TokenCSPRNGcrypto.getRandomValues()不可預測性
科學實驗TRNGRDRAND / random.org真實熵源

7. 隨機性的哲學與現實意義

從更深的層次看,「真隨機」在物理學上也是一個複雜的問題。在量子力學中,粒子的某些測量結果(如光子通過偏振片的路徑)被認為是本質上不確定的——這是目前物理學所知最接近「真隨機」的現象。相比之下,古典物理中的所謂「隨機」(如擲骰子)理論上是完全可預測的:如果你知道骰子的精確初速度、空氣阻力、桌面彈性係數,就能計算出結果。

但對於工程實踐而言,這個哲學層面的討論幾乎無關緊要。實際上重要的是:攻擊者能否在可接受的計算時間內預測輸出? 現代 CSPRNG 的設計目標就是讓這個問題的答案為「否」,而不是追求形而上學意義上的「真隨機」。

8. 小結

電腦產生亂數的機制,從表面的「點一下就出現數字」背後,隱藏著豐富的技術層次:

  • PRNG(偽隨機):確定性演算法,速度快、統計品質好,但本質可預測——適合遊戲、模擬,不適合密碼學
  • TRNG(真隨機):從物理世界收集熵,真正不可預測——速度慢,用於提供種子或特殊需求
  • CSPRNG(密碼學安全亂數):結合 PRNG 的速度與熵源的不可預測性,滿足密碼學安全要求——密碼、金鑰、Token 的唯一正確選擇

下次你使用密碼產生器或看到「隨機」功能時,可以問一個問題:這個場景需要的是「看起來隨機」,還是「真的無法預測」?這個區別,可能就是你資料安全的邊界所在。