web-dev-qa-db-ja.com

「常に/ dev / urandomを使用する」は、コンテナーと分離の時代においてまだ良いアドバイスですか?

つまり、/dev/randomの代わりに/dev/urandomをいつ使用するかを尋ねる別の質問の代わりに、次のシナリオを示します。このシナリオでは、自分が構築しているアプリケーションの中で自分自身を見つけます。

  • VMまたはコンテナー環境(つまり、新規インストール。アプリケーションが初めて実行されてからおそらく数秒しか経過していない)
  • インストールの残りの期間(数か月以上)のキーイングマテリアルとして使用する暗号化された安全なランダムバイトの必要性
  • ブロッキング(必要な場合は数分でも)が許容されるユーザーストーリーとインターフェイス

疑問に思っています。これは、ブロッキングのランダムソース(つまり、ブロッキングモードフラグでgetrandomを使用すること)のまれですが適切な使用例ですか?

長い形式:

明らかに/dev/urandom vs /dev/randomは、議論の余地のある話題になっているトピックです。私にとっては、/dev/urandomがほとんどすべての典型的なユースケースで望ましいと思います-実際、私は文字通りブロッキングランダムソースを使用したことがありません。

この人気のあるすばらしい答え の場合、Thomas Porninは、urandomのマニュアルページが誤解を招く(同意する)と、適切にシードされるとurandomのプールを主張します。実際のシナリオでエントロピーが「なくなる」ことはありません。これは私の理解にも適合しています。

しかし、「エントロピーが低いために/dev/urandomがセキュリティの問題を示唆する可能性があるのは、新しい自動OSインストールの最初の瞬間だけである」と言って、彼はurandomをわずかに売り越していると思います。

私の理解では、典型的なUbuntuサーバーブートの「ブート時のエントロピーホール」は1分以上の長さです。これは J。Alex Haldermanによるミシガン大学での研究 に基づいています。

Haldermanはまた、エントロピープールはブートごとに満たすと述べているようで、Porninが彼の回答で述べているように、最初のOSインストールではそうではありません。私のアプリケーションではそれほど重要ではありませんが、疑問に思っています。

私は "Thomas about Urandom"の投稿by ThomasHühn を読みましたが、いくつかの理由で納得できません。私のアプリケーションにとって最も適切なのは、投稿が基本的に「人々がしたくない彼らは回避策を考案し、奇妙な策略を巧みに駆使してそれを実行させるだけだ」と語った。これは間違いなく真実です(そして、私が常に他の場所で、特にWebのことで/dev/urandomを常に使用している理由です)、特に、ユーザーがそれを初めて。

端末設定でローカルに実行することを意図したアプリケーションを構築していますが、最初のインストールプロセスが少し複雑になると予想する理由はすでにあります。繰り返しのキーペアに対して少しでも堅牢性を追加できる場合は、少し待つようにユーザーに要求することに何の不安もありません。

実際、ハルダーマンは、キーペアの生成に弱いエントロピープールが使用されたため、スキャンしたホストの1%以上-105,728のSSHホストの秘密鍵を計算できたと述べています。この場合、エントロピーの源がひどく、そのためプールを埋めることが困難なのは、主に組み込みデバイスでした。

しかし、これはおそらく私の質問の中心です-アプリが完全に素朴なコンテナーで出荷される時代では、まるで数秒前の光沢のある新しいOSインストールで実行するように意図されているので、これと同じことを私たちは合理的に心配していません現象?実用的なブロッキングインターフェイスは必要ありませんか?そして、それはgetrandomが意図されているものですか?

もちろん、ホストからゲストへのエントロピーを共有することは、多くの状況で可能です。ただし、この質問の目的上、作者が展開の詳細を十分に制御できないか、ホストに利用可能なエントロピープールがないため、作者がそれを行わないと決定したと仮定しましょう。

少し先に考えてみましょう。上で説明したのと同じくらい新鮮で素朴な環境で、最初のエントロピー生成のかなりひどい見通しのデバイスで実行されるベストプラクティスは何ですか。私は、HIDがほとんどない、またはまったくない小型の組み込みデバイスを考えています。これらは、おそらく初期インストールプロセスでエアギャップが発生しています。

編集:更新:PEP524の時点では、Python(問題のアプリが記述されている)はos.urandomが呼び出されたときにgetrandomを使用し、エントロピープールが少なくとも128ビットを収集していないというイベント。だから実際問題として、私は私の答えがあると思います-os.urandomを使用するだけで、/dev/randomonlyのように動作します必要。しかし、私はここでの包括的な質問に興味があります(つまり、コンテナー化の時代は「常に使用するだけの正統派」の正統性を再考することを意味しますか)。

27
jMyles

私は answer を記述しました。これは、getrandom()が初期エントロピーを待機する方法を詳細に説明しています。

ただし、「エントロピーが低いために/ dev/urandomがセキュリティの問題を示唆する可能性があるのは、新しく自動化されたOSインストールの最初の瞬間だけである」と言って、少し乱暴に売れたと思います。

あなたの心配は根拠のあるものです。私はそのこと自体とその意味合いについて、オープンな 質問 を持っています。問題は、永続的なランダムシードが入力プールから出力プール(ブロッキングプールとCRNG)に移動するのにかなりの時間がかかることです。この問題は、_/dev/urandom_が起動後数分間、予測可能な値を出力することを意味します。ソリューションは、あなたが言うように、ブロッキング_/dev/random_を使用するか、ブロックするgetrandom()セットを使用することです。

実際、初期ブート時にカーネルのログに次のような行が表示されることは珍しくありません。

_random: sn: uninitialized urandom read (4 bytes read, 7 bits of entropy available)
random: sn: uninitialized urandom read (4 bytes read, 15 bits of entropy available)
random: sn: uninitialized urandom read (4 bytes read, 16 bits of entropy available)
random: sn: uninitialized urandom read (4 bytes read, 16 bits of entropy available)
random: sn: uninitialized urandom read (4 bytes read, 20 bits of entropy available)
_

これらはすべて、十分なエントロピーが収集される前でも、非ブロッキングプールがアクセスされた場合の例です。問題は、この時点では、エントロピーの量が少なすぎて、暗号で十分に安全ではないことです。 2つあるはず32 可能な4バイト値、ただし7ビットのエントロピーしか利用できない場合、つまり2バイトしかありません7、または128、異なる可能性。

Haldermanはまた、エントロピープールは各ブートでいっぱいになると言っているようで、Porninが彼の回答で言っているように、最初のOSインストール時ではありません。私のアプリケーションではそれほど重要ではありませんが、疑問に思っています。

それは実際には意味論の問題です。実際のエントロピーpool(カーネルに保持されているメモリのページで、ランダムな値が含まれています)は、永続的なエントロピーシードと環境ノイズによって各ブートで満たされます。ただし、エントロピーseed自体は、インストール時に作成され、システムがシャットダウンするたびに新しいランダムな値で更新されるファイルです。ポーリンがランダムシードをエントロピープールの一部(一般的なエントロピー分散および収集システムの一部など)と見なしているのに対し、ハルダーマンはそれを分離していると見なしている(エントロピープールは技術的にはメモリ、それ以上)。真実は、エントロピーシードが各ブートでエントロピープールに供給されることですが、実際にプールに影響を与えるには数分かかる場合があります。

ランダム性の3つの原因の要約:

  1. _/dev/random_-ブロッキングキャラクターデバイスは、読み込まれるたびに「エントロピーカウント」をデクリメントします(実際にはエントロピーが減っていないにもかかわらず)。ただし、起動時に十分なエントロピーが収集されるまでブロックされるため、早い段階で安全に使用できます。

  2. _/dev/urandom_-ノンブロッキングキャラクターデバイスは、誰かがデバイスを読み取るたびにランダムデータを出力します。十分なエントロピーが収集されると、ランダムデータと区別がつかない事実上無制限のストリームが出力されます。残念ながら、互換性の理由から、十分な1回限りのエントロピーが収集される前のブートの早い段階でも読み取り可能です。

  3. getrandom()-エントロピープールが必要な最小のエントロピーで適切に初期化されている限り、ランダムデータを出力するsyscall。デフォルトでは、非ブロッキングプールから読み取ります。 _GRND_NONBLOCK_フラグを指定すると、十分なエントロピーがない場合にエラーが返されます。 _GRND_RANDOM_フラグを指定すると、_/dev/random_と同じように動作し、使用可能なエントロピーが得られるまで単にブロックされます。

3番目のオプションであるgetrandom() syscallを使用することをお勧めします。これにより、プロセスは暗号で保護されたランダムデータを高速で読み取ることができ、十分なエントロピーが収集されていない起動時の早い段階でのみブロックされます。 Pythonのos.urandom()関数が、このシステムコールのラッパーとして機能する場合は、使用しても問題ありません。それが実際にあるべきかどうかについて実際に 多くの議論 があったように見え、十分なエントロピーが利用可能になるまでブロックしてしまうことになります。

少し先に考えてみましょう。上で説明したのと同じくらい新鮮で素朴な環境で、最初のエントロピー生成のかなりひどい見通しのデバイスで実行されるベストプラクティスは何ですか。

これは一般的な状況であり、それに対処するにはいくつかの方法があります。

  • たとえば_/dev/random_またはgetrandom()を使用するなどして、ブートの初期にブロックするようにしてください。

  • 可能であれば(つまり、起動ごとにストレージに書き込むことができる場合)、永続的なランダムシードを保持します。

  • 最も重要なのは、ハードウェアRNGを使用することです。これは、最も効果的な対策の1番目です。

ハードウェア乱数ジェネレータを使用することは非常に重要です。 Linuxカーネルは、サポートされているHWRNGインターフェースが存在する場合、そのインターフェースでエントロピープールを初期化し、ブートエントロピーホールを完全に排除します。多くの組み込みデバイスには、独自のランダムネスジェネレーターがあります。

これは、カーネルが環境ノイズからエントロピーを安全に生成するために必要な高解像度タイマーがない場合があるため、多くの組み込みデバイスにとって特に重要です。たとえば、一部のバージョンのMIPSプロセッサーにはサイクルカウンターがありません。

どのようにそしてなぜあなたは(私はユーザーランドを推測しますか?)CSPRNGをシードするためにurandomを使用することを提案しますか?これはどうやってgetrandomを打ちますか?

ノンブロッキングランダムネスデバイスは、高性能用に設計されていません。最近まで、ストリーム暗号ではなくランダム暗号化にSHA-1を使用しているため、デバイスは非常に低速でした。カーネルインターフェイスをランダムに使用すると、ローカルのユーザー空間CSPRNGよりも効率が低下する可能性があります。これは、カーネルを呼び出すたびに、高価な コンテキストスイッチ が必要になるためです。カーネルは、そこから大量に引き出したいアプリケーションを考慮に入れるように設計されていますが、 ソースコード のコメントは、これを正しいことと見なしていないことを明確にしています。

_/*
 * Hack to deal with crazy userspace progams when they are all trying
 * to access /dev/urandom in parallel.  The programs are almost
 * certainly doing something terribly wrong, but we'll work around
 * their brain damage.
 */
_

OpenSSLなどの一般的な暗号ライブラリ ランダムデータの生成をサポート 。それらは一度シードするか、時々再シードすることができ、並列化からより多くの利益を得ることができます。さらに、特定のオペレーティングシステムまたはオペレーティングシステムのバージョンの動作に依存しないポータブルコードの記述を可能にします。

大量のランダム性が必要ない場合は、カーネルのインターフェースを使用することで完全に問題ありません。存続期間を通して多くのランダム性を必要とする暗号アプリケーションを開発している場合は、OpenSSLのようなライブラリを使用してそれを処理することができます。

22
forest

システムの状態には次の3つがあります。

  1. CPRNGを安全に初期化するのに十分なエントロピーを収集していません。
  2. CPRNGを安全に初期化するのに十分なエントロピーを収集しました。

    2a。収集した以上のエントロピーを出しました。

    2b。収集したエントロピーよりも少ないエントロピーを与えています。

歴史的に、人々は(2a)と(2b)の区別が重要であると考えていました。これにより2つの問題が発生しました。まず、それは間違っています。適切に設計されたCPRNGにとって、区別は意味がありません。次に、(2a)と(2b)の違いに重点を置くと、(1)と(2)の違いを見逃してしまいますが、これは実際には非常に重要です。人々は(1)が(2a)の特別なケースになるように崩壊しただけです。

本当に必要なのは、状態(1)でブロックし、状態(2a)または(2b)でブロックしないものです。

残念ながら、昔は(1)と(2a)の混同により、これは選択肢ではありませんでした。唯一の2つのオプションは、ケース(1)および(2a)でブロックされた/dev/randomと、ブロックされなかった/dev/urandomです。しかし、状態(1)はほとんど発生しません。適切に構成されたシステムでは発生しません。以下を参照してください。ほとんどすべてのシステムで、ほぼ常に/dev/urandomの方が適しています。ここで、「常にウランダムを使用する」に関するすべてのブログの投稿がありました。彼らは、人々に(2a)と(2b)の状態を無意味で有害な区別をするのをやめるよう説得しようとしていました。

しかし、そうです、どちらも実際に必要なものではありません。したがって、新しいgetrandom syscallは、デフォルトで状態(1)でブロックし、状態(2a)または(2b)ではブロックしません。したがって、最新のLinuxでは、正統性を次のように更新する必要があります常にデフォルト設定でgetrandomを使用

余分なしわ:

  • getrandomは、/dev/randomのように機能するデフォルト以外のモードもサポートします。これは、GRND_RANDOMフラグを介して要求できます。私の知る限り、これらの古いブログ投稿が説明したのと同じ理由で、このフラグが実際に役立つことはありません。使用しないでください。

  • getrandomには、/dev/urandomに比べていくつかの特別な利点があります。ファイルシステムレイアウトに関係なく機能し、ファイル記述子を開く必要がありません。どちらも、最小限にしたい汎用ライブラリには問題があります。暗号化のセキュリティには影響しませんが、運用上は問題ありません。

  • 適切に構成されたシステムは、初期ブート時でも常にエントロピーを利用できます(つまり、これまで状態(1)になることは決してないはずです)。これを管理する方法はたくさんあります。前回の起動時のエントロピーを保存して、次の起動時に使用します。ハードウェアRNGをインストールします。 Dockerコンテナはホストのカーネルを使用するため、エントロピープールにアクセスできます。高品質の仮想化設定には、ハイパーバイザーインターフェイスを介してゲストシステムにホストシステムからエントロピーをフェッチさせる方法があります(「virtio rng」の検索など)。しかしもちろん、すべてのシステムが適切に構成されているわけではありません。システムの構成が適切でない場合は、適切に構成できるかどうかを確認してください。原則として、これは安く済むはずですが、実際にはセキュリティを優先しないため、クラウドプロバイダーの切り替えや別の組み込みプラットフォームへの切り替えなどが必要になる場合があります。そして残念なことに、これはあなた(またはあなたの上司)が支払う意思があるよりも高価であることに気づくかもしれません。そのため、不適切に構成されたシステムを扱うのに行き詰まっています。もしそうなら私の同情。

  • @forestが注記しているように、多くのCPRNG値が必要な場合は、非常に注意深く、(再)シードにgetrandomを使用しながら、ユーザー空間で独自のCPRNGを実行することでこれを高速化できます。ただし、これは、独自の暗号プリミティブを実装している状況と同じように、「専門家のみ」のものです。測定して、getrandomを直接使用するとニーズに遅すぎる、および重要な暗号を使用している場合にのみ、これを行う必要があります。専門知識。セキュリティが完全に破壊されるような方法でCPRNG実装を台無しにするのは非常に簡単ですが、出力はまだランダムに見えるため、気付かないでしょう。

9