關於 /dev/urandom 的流言終結

有不少關於 /dev/urandom/dev/random 的流言在坊間不斷流傳。然而流言終究是流言。html

本篇文章裏針對的都是近來的 Linux 操做系統,其它類 Unix 操做系統不在討論範圍內。linux

/dev/urandom 不安全。加密用途必須使用 /dev/randomgit

事實/dev/urandom 纔是類 Unix 操做系統下推薦的加密種子。程序員

/dev/urandom僞隨機數生成器pseudo random number generator(PRND),而 /dev/random 是「真」隨機數生成器。github

事實:它們二者本質上用的是同一種 CSPRNG (一種密碼學僞隨機數生成器)。它們之間細微的差異和「真」「不真」隨機徹底無關。(參見:「Linux 隨機數生成器的構架」一節)算法

/dev/random 在任何狀況下都是密碼學應用更好地選擇。即使 /dev/urandom 也一樣安全,咱們仍是不該該用它。shell

事實/dev/random 有個很噁心人的問題:它是阻塞的。(參見:「阻塞有什麼問題?」一節)(LCTT 譯註:意味着請求都得逐個執行,等待前一個請求完成)安全

但阻塞不是好事嗎!/dev/random 只會給出電腦收集的信息熵足以支持的隨機量。/dev/urandom 在用完了全部熵的狀況下還會不斷吐出不安全的隨機數給你。ruby

事實:這是誤解。就算咱們不去考慮應用層面後續對隨機種子的用法,「用完信息熵池」這個概念自己就不存在。僅僅 256 位的熵就足以生成計算上安全的隨機數很長、很長的一段時間了。(參見:「那熵池快空了的狀況呢?」一節)服務器

問題的關鍵還在後頭:/dev/random 怎麼知道有系統會多少可用的信息熵?接着看!

但密碼學家總是討論從新選種子(re-seeding)。這難道不和上一條衝突嗎?

事實:你說的也沒錯!某種程度上吧。確實,隨機數生成器一直在使用系統信息熵的狀態從新選種。但這麼作(一部分)是由於別的緣由。(參見:「從新選種」一節)

這樣說吧,我沒有說引入新的信息熵是壞的。更多的熵確定更好。我只是說在熵池低的時候阻塞是不必的。

好,就算你說的都對,可是 /dev/(u)random 的 man 頁面和你說的也不同啊!到底有沒有專家贊成你說的這堆啊?

事實:其實 man 頁面和我說的不衝突。它看似好像在說 /dev/urandom 對密碼學用途來講不安全,但若是你真的理解這堆密碼學術語你就知道它說的並非這個意思。(參見:「random 和 urandom 的 man 頁面」一節)

man 頁面確實說在一些狀況下推薦使用 /dev/random (我以爲也沒問題,但絕對不是說必要的),但它也推薦在大多數「通常」的密碼學應用下使用 /dev/urandom

雖然訴諸權威通常來講不是好事,但在密碼學這麼嚴肅的事情上,和專家統一意見是頗有必要的。

因此說呢,還確實有一些專家和個人一件事一致的:/dev/urandom 就應該是類 UNIX 操做系統下密碼學應用的首選。顯然的,是他們的觀點說服了我而不是反過來的。(參見:「正道」一節)


難以相信嗎?以爲我確定錯了?讀下去看我能不能說服你。

我嘗試不講過高深的東西,可是有兩點內容必須先提一下才能讓咱們接着論證觀點。

首當其衝的,什麼是隨機性,或者更準確地:咱們在探討什麼樣的隨機性?(參見:「真隨機」一節)

另一點很重要的是,我沒有嘗試以說教的態度對大家寫這段話。我寫這篇文章是爲了往後能夠在討論起的時候指給別人看。比 140 字長(LCTT 譯註:推特長度)。這樣我就不用一遍遍重複個人觀點了。能把論點磨鍊成一篇文章自己就頗有助於未來的討論。(參見:「你是在說我笨?!」一節)

而且我很是樂意聽到不同的觀點。但我只是認爲單單地說 /dev/urandom 壞是不夠的。你得能指出到底有什麼問題,而且剖析它們。

你是在說我笨?!

絕對沒有!

事實上我本身也相信了 「/dev/urandom 是不安全的」 好些年。這幾乎不是咱們的錯,由於那麼德高望重的人在 Usenet、論壇、推特上跟咱們重複這個觀點。甚至連 man 手冊都似是而非地說着。咱們當年怎麼可能鄙視諸如「信息熵過低了」這種看上去就很讓人信服的觀點呢?(參見:「random 和 urandom 的 man 頁面」一節)

整個流言之因此如此廣爲流傳不是由於人們太蠢,而是由於但凡是有點關於信息熵和密碼學概念的人都會以爲這個說法頗有道理。直覺彷佛都在告訴咱們這流言講的頗有道理。很不幸直覺在密碼學裏一般無論用,此次也同樣。

真隨機

隨機數是「真正隨機」是什麼意思?

我不想搞的太複雜以致於變成哲學範疇的東西。這種討論很容易走偏由於對於隨機模型你們見仁見智,討論很快變得毫無心義。

在我看來「真隨機」的「試金石」是量子效應。一個光子穿過或不穿過一個半透鏡。或者觀察一個放射性粒子衰變。這類東西是現實世界最接近真隨機的東西。固然,有些人也不相信這類過程是真隨機的,或者這個世界根本不存在任何隨機性。這個就百家爭鳴了,我也很差多說什麼了。

密碼學家通常都會經過不去討論什麼是「真隨機」來避免這種哲學辯論。他們更關心的是不可預測性unpredictability。只要沒有任何方法能猜出下一個隨機數就能夠了。因此當你以密碼學應用爲前提討論一個隨機數好很差的時候,在我看來這纔是最重要的。

不管如何,我不怎麼關心「哲學上安全」的隨機數,這也包括別人嘴裏的「真」隨機數。

兩種安全,一種有用

但就讓咱們退一步說,你有了一個「真」隨機變量。你下一步作什麼呢?

你把它們打印出來而後掛在牆上來展現量子宇宙的美與和諧?牛逼!我支持你。

可是等等,你說你要它們?作密碼學用途?額,那這就廢了,由於這事情就有點複雜了。

事情是這樣的,你的真隨機、量子力學加護的隨機數即將被用進不理想的現實世界算法裏去。

由於咱們使用的幾乎全部的算法都並非信息論安全性information-theoretic security 的。它們「只能」提供計算意義上的安全。我能想到爲數很少的例外就只有 Shamir 密鑰分享和一次性密碼本One-time pad(OTP)算法。而且就算前者是名副其實的(若是你實際打算用的話),後者則毫無可行性可言。

但全部那些大名鼎鼎的密碼學算法,AES、RSA、Diffie-Hellman、橢圓曲線,還有全部那些加密軟件包,OpenSSL、GnuTLS、Keyczar、你的操做系統的加密 API,都僅僅是計算意義上安全的。

那區別是什麼呢?信息論安全的算法確定是安全的,絕對是,其它那些的算法均可能在理論上被擁有無限計算力的窮舉破解。咱們依然愉快地使用它們是由於全世界的計算機加起來都不可能在宇宙年齡的時間裏破解,至少如今是這樣。而這就是咱們文章裏說的「不安全」。

除非哪一個聰明的傢伙破解了算法自己 —— 在只須要更少許計算力、在今天可實現的計算力的狀況下。這也是每一個密碼學家求之不得的聖盃:破解 AES 自己、破解 RSA 自己等等。

因此如今咱們來到了更底層的東西:隨機數生成器,你堅持要「真隨機」而不是「僞隨機」。可是沒過一下子你的真隨機數就被喂進了你極爲鄙視的僞隨機算法裏了!

真相是,若是咱們最早進的哈希算法被破解了,或者最早進的分組加密算法被破解了,你獲得的這些「哲學上不安全」的隨機數甚至無所謂了,由於反正你也沒有安全的應用方法了。

因此把計算性上安全的隨機數餵給你的僅僅是計算性上安全的算法就能夠了,換而言之,用 /dev/urandom

Linux 隨機數生成器的構架

一種錯誤的見解

你對內核的隨機數生成器的理解極可能是像這樣的:

image: mythical structure of the kernel's random number generator

「真正的隨機性」,儘管可能有點瑕疵,進入操做系統而後它的熵馬上被加入內部熵計數器。而後通過「矯偏」和「漂白」以後它進入內核的熵池,而後 /dev/random/dev/urandom 從裏面生成隨機數。

「真」隨機數生成器,/dev/random,直接從池裏選出隨機數,若是熵計數器表示能知足須要的數字大小,那就吐出數字而且減小熵計數。若是不夠的話,它會阻塞程序直至有足夠的熵進入系統。

這裏很重要一環是 /dev/random 幾乎只是僅通過必要的「漂白」後就直接把那些進入系統的隨機性吐了出來,不經扭曲。

而對 /dev/urandom 來講,事情是同樣的。除了當沒有足夠的熵的時候,它不會阻塞,而會從一直在運行的僞隨機數生成器(固然,是密碼學安全的,CSPRNG)裏吐出「低質量」的隨機數。這個 CSPRNG 只會用「真隨機數」生成種子一次(或者好幾回,這不重要),但你不能特別相信它。

在這種對隨機數生成的理解下,不少人會以爲在 Linux 下儘可能避免 /dev/urandom 看上去有那麼點道理。

由於要麼你有足夠多的熵,你會至關於用了 /dev/random。要麼沒有,那你就會從幾乎沒有高熵輸入的 CSPRNG 那裏獲得一個低質量的隨機數。

看上去很邪惡是吧?很不幸的是這種見解是徹底錯誤的。實際上,隨機數生成器的構架更像是下面這樣的。

更好地簡化

Linux 4.8 以前

image: actual structure of the kernel's random number generator before Linux 4.8

你看到最大的區別了嗎?CSPRNG 並非和隨機數生成器一塊兒跑的,它在 /dev/urandom 須要輸出但熵不夠的時候進行填充。CSPRNG 是整個隨機數生成過程的內部組件之一。歷來就沒有什麼 /dev/random 直接從池裏輸出純純的隨機性。每一個隨機源的輸入都在 CSPRNG 裏充分混合和散列過了,這一切都發生在實際變成一個隨機數,被 /dev/urandom 或者 /dev/random 吐出去以前。

另一個重要的區別是這裏沒有熵計數器的任何事情,只有預估。一個源給你的熵的量並非什麼很明確能直接獲得的數字。你得預估它。注意,若是你太樂觀地預估了它,那 /dev/random 最重要的特性——只給出熵容許的隨機量——就蕩然無存了。很不幸的,預估熵的量是很困難的。

這是個很粗糙的簡化。實際上不只有一個,而是三個熵池。一個主池,另外一個給 /dev/random,還有一個給 /dev/urandom,後二者依靠從主池裏獲取熵。這三個池都有各自的熵計數器,但二級池(後兩個)的計數器基本都在 0 附近,而「新鮮」的熵總在須要的時候從主池流過來。同時還有好多混合和迴流進系統在同時進行。整個過程對於這篇文檔來講都過於複雜了,咱們跳過。

Linux 內核只使用事件的到達時間來預估熵的量。根據模型,它經過多項式插值來預估實際的到達時間有多「出乎意料」。這種多項式插值的方法究竟是不是好的預估熵量的方法自己就是個問題。同時硬件狀況會不會以某種特定的方式影響到達時間也是個問題。而全部硬件的取樣率也是個問題,由於這基本上就直接決定了隨機數到達時間的顆粒度。

說到最後,至少如今看來,內核的熵預估仍是不錯的。這也意味着它比較保守。有些人會具體地討論它有多好,這都超出個人腦容量了。就算這樣,若是你堅持不想在沒有足夠多的熵的狀況下吐出隨機數,那你看到這裏可能還會有一絲緊張。我睡的就很香了,由於我不關心熵預估什麼的。

最後要明確一下:/dev/random/dev/urandom 都是被同一個 CSPRNG 飼餵的。只有它們在用完各自熵池(根據某種預估標準)的時候,它們的行爲會不一樣:/dev/random 阻塞,/dev/urandom 不阻塞。

Linux 4.8 之後

image: actual structure of the kernel's random number generator from Linux 4.8 onward

在 Linux 4.8 裏,/dev/random/dev/urandom 的等價性被放棄了。如今 /dev/urandom 的輸出不來自於熵池,而是直接從 CSPRNG 來。

咱們很快會理解爲何這不是一個安全問題。(參見:「CSPRNG 沒問題」一節)

阻塞有什麼問題?

你有沒有須要等着 /dev/random 來吐隨機數?好比在虛擬機裏生成一個 PGP 密鑰?或者訪問一個在生成會話密鑰的網站?

這些都是問題。阻塞本質上會下降可用性。換而言之你的系統不干你讓它乾的事情。不用我說,這是很差的。要是它不幹活你幹嗎搭建它呢?

我在工廠自動化裏作過和安全相關的系統。猜猜看安全系統失效的主要緣由是什麼?操做問題。就這麼簡單。不少安全措施的流程讓工人惱火了。好比時間太長,或者太不方便。你要知道人很會找捷徑來「解決」問題。

但其實有個更深入的問題:人們不喜歡被打斷。它們會找一些繞過的方法,把一些詭異的東西接在一塊兒僅僅由於這樣能用。通常人根本不知道什麼密碼學什麼亂七八糟的,至少正常的人是這樣吧。

爲何不由止調用 random()?爲何不隨便在論壇上找我的告訴你用寫奇異的 ioctl 來增長熵計數器呢?爲何不乾脆就把 SSL 加密給關了算了呢?

到頭來若是東西太難用的話,你的用戶就會被迫開始作一些下降系統安全性的事情——你甚至不知道它們會作些什麼。

咱們很容易會忽視可用性之類的重要性。畢竟安全第一對吧?因此比起犧牲安全,不可用、難用、不方便都是次要的?

這種二元對立的想法是錯的。阻塞不必定就安全了。正如咱們看到的,/dev/urandom 直接從 CSPRNG 裏給你同樣好的隨機數。用它很差嗎!

CSPRNG 沒問題

如今狀況聽上去很慘淡。若是連高質量的 /dev/random 都是從一個 CSPRNG 裏來的,咱們怎麼敢在高安全性的需求上使用它呢?

實際上,「看上去隨機」是現存大多數密碼學基礎組件的基本要求。若是你觀察一個密碼學哈希的輸出,它必定得和隨機的字符串不可區分,密碼學家纔會承認這個算法。若是你生成一個分組加密,它的輸出(在你不知道密鑰的狀況下)也必須和隨機數據不可區分才行。

若是任何人能比暴力窮舉要更有效地破解一個加密,好比它利用了某些 CSPRNG 僞隨機的弱點,那這就又是老一套了:一切都廢了,也別談後面的了。分組加密、哈希,一切都是基於某個數學算法,好比 CSPRNG。因此別懼怕,到頭來都同樣。

那熵池快空了的狀況呢?

毫無影響。

加密算法的根基創建在攻擊者不能預測輸出上,只要最一開始有足夠的隨機性(熵)就好了。「足夠」的下限能夠是 256 位,不須要更多了。

介於咱們一直在很隨意的使用「熵」這個概念,我用「位」來量化隨機性但願讀者不要太在乎細節。像咱們以前討論的那樣,內核的隨機數生成器甚至無法精確地知道進入系統的熵的量。只有一個預估。並且這個預估的準確性到底怎麼樣也沒人知道。

從新選種

但若是熵這麼不重要,爲何還要有新的熵一直被收進隨機數生成器裏呢?

djb 提到 太多的熵甚至可能會起到反效果。

首先,通常不會這樣。若是你有不少隨機性能夠拿來用,用就對了!

但隨機數生成器時不時要從新選種還有別的緣由:

想象一下若是有個攻擊者獲取了你隨機數生成器的全部內部狀態。這是最壞的狀況了,本質上你的一切都暴露給攻擊者了。

你已經涼了,由於攻擊者能夠計算出全部將來會被輸出的隨機數了。

可是,若是不斷有新的熵被混進系統,那內部狀態會再一次變得隨機起來。因此隨機數生成器被設計成這樣有些「自愈」能力。

但這是在給內部狀態引入新的熵,這和阻塞輸出沒有任何關係。

random 和 urandom 的 man 頁面

這兩個 man 頁面在嚇唬程序員方面頗有建樹:

/dev/urandom 讀取數據不會由於須要更多熵而阻塞。這樣的結果是,若是熵池裏沒有足夠多的熵,取決於驅動使用的算法,返回的數值在理論上有被密碼學攻擊的可能性。發動這樣攻擊的步驟並無出如今任何公開文獻當中,但這樣的攻擊從理論上講是可能存在的。若是你的應用擔憂這類狀況,你應該使用 /dev/random

實際上已經有 /dev/random/dev/urandom 的 Linux 內核 man 頁面的更新版本。不幸的是,隨便一個網絡搜索出現我在結果頂部的仍然是舊的、有缺陷的版本。此外,許多 Linux 發行版仍在發佈舊的 man 頁面。因此不幸的是,這一節須要在這篇文章中保留更長的時間。我很期待刪除這一節!

沒有「公開的文獻」描述,可是 NSA 的小賣部裏確定賣這種攻擊手段是吧?若是你真的真的很擔憂(你應該很擔憂),那就用 /dev/random 而後全部問題都沒了?

然而事實是,可能某個什麼情報局有這種攻擊,或者某個什麼邪惡黑客組織找到了方法。但若是咱們就直接假設這種攻擊必定存在也是不合理的。

並且就算你想給本身一個安心,我要給你潑個冷水:AES、SHA-3 或者其它什麼常見的加密算法也沒有「公開文獻記述」的攻擊手段。難道你也不用這幾個加密算法了?這顯然是好笑的。

咱們在回到 man 頁面說:「使用 /dev/random」。咱們已經知道了,雖然 /dev/urandom 不阻塞,可是它的隨機數和 /dev/random 都是從同一個 CSPRNG 裏來的。

若是你真的須要信息論安全性的隨機數(你不須要的,相信我),那纔有可能成爲惟一一個你須要等足夠熵進入 CSPRNG 的理由。並且你也不能用 /dev/random

man 頁面有毒,就這樣。但至少它還稍稍挽回了一下本身:

若是你不肯定該用 /dev/random 仍是 /dev/urandom ,那你可能應該用後者。一般來講,除了須要長期使用的 GPG/SSL/SSH 密鑰之外,你總該使用/dev/urandom

該手冊頁的當前更新版本絕不含糊地說:

/dev/random 接口被認爲是遺留接口,而且 /dev/urandom 在全部用例中都是首選和足夠的,除了在啓動早期須要隨機性的應用程序;對於這些應用程序,必須替代使用 getrandom(2),由於它將阻塞,直到熵池初始化完成。

行。我以爲不必,但若是你真的要用 /dev/random 來生成 「長期使用的密鑰」,用就是了也沒人攔着!你可能須要等幾秒鐘或者敲幾下鍵盤來增長熵,但這沒什麼問題。

但求求大家,不要就由於「你想更安全點」就讓連個郵件服務器要掛起半天。

正道

本篇文章裏的觀點顯然在互聯網上是「小衆」的。但若是問一個真正的密碼學家,你很難找到一個認同阻塞 /dev/random 的人。

好比咱們看看 Daniel Bernstein(即著名的 djb)的見解:

咱們密碼學家對這種胡亂迷信行爲表示不負責。你想一想,寫 /dev/random man 頁面的人好像同時相信:

  • (1) 咱們不知道如何用一個 256 位長的 /dev/random 的輸出來生成一個無限長的隨機密鑰串流(這是咱們須要 /dev/urandom 吐出來的),但與此同時
  • (2) 咱們卻知道怎麼用單個密鑰來加密一條消息(這是 SSL,PGP 之類乾的事情)

對密碼學家來講這甚至都很差笑了

或者 Thomas Pornin 的見解,他也是我在 stackexchange 上見過最樂於助人的一位:

簡單來講,是的。展開說,答案仍是同樣。/dev/urandom 生成的數據能夠說和真隨機徹底沒法區分,至少在現有科技水平下。使用比 /dev/urandom 「更好的「隨機性毫無心義,除非你在使用極爲罕見的「信息論安全」的加密算法。這確定不是你的狀況,否則你早就說了。

urandom 的 man 頁面多多少少有些誤導人,或者乾脆能夠說是錯的——特別是當它說 /dev/urandom 會「用完熵」以及 「/dev/random 是更好的」那幾句話;

或者 Thomas Ptacek 的見解,他不設計密碼算法或者密碼學系統,但他是一家名聲在外的安全諮詢公司的創始人,這家公司負責不少滲透和破解爛密碼學算法的測試:

用 urandom。用 urandom。用 urandom。用 urandom。用 urandom。

沒有完美

/dev/urandom 不是完美的,問題分兩層:

在 Linux 上,不像 FreeBSD,/dev/urandom 永遠不阻塞。記得安全性取決於某個最一開始決定的隨機性?種子?

Linux 的 /dev/urandom 會很樂意給你吐點不怎麼隨機的隨機數,甚至在內核有機會收集一丁點熵以前。何時有這種狀況?當你係統剛剛啓動的時候。

FreeBSD 的行爲更正確點:/dev/random/dev/urandom 是同樣的,在系統啓動的時候 /dev/random 會阻塞到有足夠的熵爲止,而後它們都不再阻塞了。

與此同時 Linux 實行了一個新的系統調用syscall,最先由 OpenBSD 引入叫 getentrypy(2),在 Linux 下這個叫 getrandom(2)。這個系統調用有着上述正確的行爲:阻塞到有足夠的熵爲止,而後不再阻塞了。固然,這是個系統調用,而不是一個字節設備(LCTT 譯註:不在 /dev/ 下),因此它在 shell 或者別的腳本語言裏沒那麼容易獲取。這個系統調用 自 Linux 3.17 起存在。

在 Linux 上其實這個問題不太大,由於 Linux 發行版會在啓動的過程當中保存一點隨機數(這發生在已經有一些熵以後,由於啓動程序不會在按下電源的一瞬間就開始運行)到一個種子文件中,以便系統下次啓動的時候讀取。因此每次啓動的時候系統都會從上一次會話裏帶一點隨機性過來。

顯然這比不上在關機腳本里寫入一些隨機種子,由於這樣的顯然就有更多熵能夠操做了。但這樣作顯而易見的好處就是它不用關心繫統是否是正確關機了,好比可能你係統崩潰了。

並且這種作法在你真正第一次啓動系統的時候也無法幫你隨機,不過好在 Linux 系統安裝程序通常會保存一個種子文件,因此基本上問題不大。

虛擬機是另一層問題。由於用戶喜歡克隆它們,或者恢復到某個以前的狀態。這種狀況下那個種子文件就幫不到你了。

但解決方案依然和用 /dev/random 不要緊,而是你應該正確的給每一個克隆或者恢復的鏡像從新生成種子文件。

太長不看

別問,問就是用 /dev/urandom !


via: www.2uo.de/myths-about…

做者:Thomas Hühn 譯者:Moelf 校對:wxy

本文由 LCTT 原創編譯,Linux中國 榮譽推出

相關文章
相關標籤/搜索