揭穿 /dev/urandom 與 /dev/random 的迷思

多年來,開發者與系統管理員之間一直流傳著一種說法:"/dev/urandom 不安全;進行加密用途時應始終使用 /dev/random。" 這種建議經常出現在論壇、舊的教學指南中,甚至在系統 man pages 中也隱約提到。這種恐懼的核心在於「熵耗盡」(entropy exhaustion)——即認為系統會「用完」隨機性,導致您的加密金鑰變得脆弱。

事實上,這種直覺從根本上來說是錯誤的。對於類 UNIX 系統上的絕大多數加密應用程式而言,/dev/urandom 不僅安全,而且是首選。要理解其中的原因,需要深入探討 Linux 核心如何處理隨機性,以及資訊理論安全性與計算安全性之間的區別。

核心誤解:「真」隨機與「偽」隨機

許多人認為 /dev/random 提供的是直接從硬體雜訊中採集的「真」隨機性,而 /dev/urandom 則是一個偽隨機數生成器(PRNG),在「真」熵不足時填補空隙。

這是一個迷思。事實上,/dev/random/dev/urandom 都使用相同的密碼學安全偽隨機數生成器(CSPRNG)。主要的區別不在於它們產生的數字品質,而是在於當核心的熵估計值較低時它們的行為

計算安全性 vs. 資訊理論安全性

為了理解為什麼 CSPRNG 就足夠了,我們必須區分兩種安全性:

  1. 資訊理論安全性: 這是黃金標準,無論攻擊者的計算能力如何,系統都是安全的(例如:一次性密碼本 One-Time Pad)。這需要一個與訊息本身一樣長的真正隨機金鑰。
  2. 計算安全性: 這是幾乎所有現代密碼學(AES, RSA, Elliptic Curve)所依賴的。這些演算法之所以安全,是因為電腦要進行暴力破解金鑰需要天文數字般的時間——比宇宙的年齡還要長。

由於我們在加密時使用的是計算安全型的演算法,因此使用計算安全型的隨機性來源(CSPRNG)是完全合適的。如果攻擊者能夠預測 CSPRNG 的輸出,他們很可能也能破解您其餘安全基礎設施所依賴的底層區塊加密或雜湊函數。

Linux 隨機性架構如何運作

Linux 4.8 之前的時代

在 4.8 版本之前,核心會將來自各種來源(如鍵盤輸入時間與磁碟中斷)的熵混合到一個池(pool)中。這個池會為 CSPRNG 提供種子(seed)。

  • /dev/urandom 會從 CSPRNG 提取數據且永不阻塞。
  • /dev/random 會從同一個 CSPRNG 提取數據,但如果核心內部的熵估計值低於某個閾值,它就會阻塞

至關重要的是,熵的「計數」只是一個估計值。它並非對可用隨機性的精確測量。基於估計值進行阻塞並不會提供實際的安全效益,反而會引入顯著的可用性風險。

Linux 4.8 及之後版本

在 Linux 4.8 中,這種區別被進一步精確化。/dev/urandom 現在直接從 CSPRNG 提取數據。其基本原理仍然不變:一旦 CSPRNG 使用足夠的初始熵(通常為 256 bits)進行種子初始化,它就能產生幾乎無窮無盡的密碼學安全位元組流。

阻塞的危險性

因為阻塞而選擇 /dev/random 實際上可能會降低您系統的安全性。當系統因等待熵而阻塞時,會造成阻斷服務(denial-of-service)的情況。在生產環境中,這會導致:

  • 應用程式掛起: Web 伺服器在等待生成臨時會話金鑰時可能會停止回應。
  • 危險的權宜措施: 沮喪的系統管理員可能會停用 SSL/TLS、修改掉對 random() 的呼叫,或者使用不安全的 ioctl 呼叫來人為地增加熵計數,只為了讓系統恢復運作。

透過選擇 /dev/urandom,您可以在不犧牲密碼學強度的情況下,維持系統的可用性。

回應常見的反對意見

「但 man page 說了……」

/dev/random/dev/urandom 的 man pages 是出了名的模糊,經常暗示 /dev/urandom 在理論上「容易受到攻擊」。然而,正如該領域的專家所指出的,這些理論上的攻擊在非機密文獻中並未被發現,且其難度並不比攻擊 AES 本身更具實踐性。

「那重新種子(re-seeding)呢?」

系統會不斷地將新的熵注入 CSPRNG。這並不是因為生成器「用完」了隨機性,而是為了提供完美的前向安全性(Perfect Forward Secrecy)。如果攻擊者以某種方式破解了生成器的內部狀態,持續注入的新熵能確保狀態最終會重新變得隨機,從而隨著時間推移「修復」生成器。

唯一的真實弱點:啟動程序

有一種情況下 /dev/urandom 可能會產生問題:就在開機啟動後。如果核心尚未收集到足夠的熵來為 CSPRNG 進行首次種子初始化,其輸出可能是可預測的。

Linux 透過在關機時儲存種子檔案並在開機時載入來緩解此問題。然而,這對虛擬機(VM)來說可能是一個挑戰,因為複製的映像檔或還原的檢查點可能會導致多台虛擬機在啟動時使用相同的種子。解決方案並非切換到 /dev/random,而是確保每台虛擬機在複製或還原時都經過正確的種子初始化。

最終結論

正如密碼學家 Daniel Bernstein (djb) 所指出的,如果我們可以利用單一金鑰來加密許多訊息(SSL/PGP),卻無法確定性地將一個 256-bit 的種子擴展為一串不可預測的金鑰流,這本身就是一個矛盾。

"對於密碼學家來說,這甚至連笑話都算不上。"

底線:直接使用 /dev/urandom

Sources