web-dev-qa-db-ja.com

「機密性の高い」変数を削除する前に上書きすることは、安全なプログラミング手法として適切でしょうか?

変数に格納された機密データが削除される(またはスコープから外れる)前に、機密データを上書きすることは、安全なプログラミング手法ですか?私の考えでは、ハッカーがRAMでデータの残留性のために潜在データを読み取ることができなくなるでしょう。数回上書きすることでセキュリティが強化されますか?ここに小さなC++で私が話している例(コメントを含む)。

void doSecret()
{
  // The secret you want to protect (probably best not to have it hardcoded like this)
  int mySecret = 12345;

  // Do whatever you do with the number
  ...

  // **Clear out the memory of mySecret by writing over it**
  mySecret = 111111;
  mySecret = 0;
  // Maybe repeat a few times in a loop
}

これが実際にセキュリティを追加する場合、コンパイラがこれを実行するための命令を自動的に追加すると(おそらくデフォルトで、または変数を削除するときにコンパイラに実行するように指示することによって)いいと思います。


この質問は今週の情報セキュリティの質問として取り上げられました。
詳細については、2014年12月12日ブログエントリをお読みください。または今週の質問を送信してください

75
Jonathan

はい、上書きしてから、値を削除/解放することをお勧めします。 各言語が相互作用するため、実行する必要があるのは「データを上書きする」こと、またはGCが処理する範囲から外れることを前提としないハードウェアとは異なります。

変数を保護する場合、次のことを考慮する必要があります。

  • 暗号化(メモリダンプまたはページキャッシュの場合)
  • メモリへの固定
  • 読み取り専用としてマークする機能(これ以上の変更を防ぐため)
  • 定数文字列を渡さないことによる安全な構築
  • コンパイラの最適化(リンクされた記事の の注記を参照 re:ZeroMemoryマクロ)

「消去」の実際の実装は、言語とプラットフォームによって異なります。使用している言語を調査し、 安全にコーディングできるかどうかを確認してください

なぜこれが良い考えなのですか?クラッシュダンプ、およびヒープを含むすべてのものに機密データが含まれる可能性があります。インメモリデータを保護する場合は、次の使用を検討してください

言語ごとの実装ガイドについては、StackOverflowを参照してください。

ベンダーガイダンス(この場合はMSFT)を使用する場合でも、 SecureStringの内容をダンプすることは引き続き可能です 、および 高セキュリティシナリオ用の特定の使用ガイドラインがある場合があります。

70

再度使用されない値を格納しますか?それが提供するかもしれないどんな利点にも関係なく、最適化される何かのように見えます。

また、言語自体の動作方法によっては、実際にメモリ内のデータを上書きできない場合があります。たとえば、ガベージコレクターを使用している言語では、すぐには削除されません(これは、他の参照がぶら下がっていないことを前提としています)。

たとえば、C#では、以下は機能しないと思います。

string secret = "my secret data";

...lots of work...

string secret = "blahblahblah";

"my secret data"は不変であるため、ガベージコレクションが行われるまで待機します。その最後の行は、実際には新しい文字列を作成しており、その秘密のポイントを持っています。実際の秘密データが削除される速度は速くなりません。

メリットはありますか?データを上書きしていることを確認できるようにアセンブリまたは何らかの低レバー言語で記述し、アプリケーションを実行したままコンピュータをスリープ状態にするか、そのままにしておくと、RAM =邪悪なメイドによってこすり取られ、邪悪なメイドは私たちのRAMシークレットが上書きされた後、それが削除される前のデータ(非常に小さなスペースの可能性があります)を取得しました。 RAMまたはハードドライブ上でこの秘密を解き放ちます...それから私はセキュリティの可能性のある増加を見ます。

しかし、コストと利益の関係で、このセキュリティ最適化は、最適化のリストで非常に低くなっているようです(そして、ほとんどのアプリケーションで一般に「価値がある」というポイントを下回っています)。

可能な限り最短の時間でそれを確実に保持するために短期間に秘密を保持することを意図した特別なチップでこれの使用が制限されるのを見る可能性がありますが、それでも私はコストの利益については不確かです。

34
Lawtonfogle

脅威モデルが必要です

どのような種類のハッキングを防止しようとしているのかを説明する脅威モデルが得られるまで、セキュリティ変数の上書きについて考え始めるべきではありません。 セキュリティには常にコストがかかります。この場合、コストは、データを保護するためにこのすべての追加コードを維持することを開発者に教える開発コストです。 このコストは、開発者がミスを犯す可能性が高く、それらのミスがメモリの問題よりもリークの原因である可能性が高いことを意味します。

  • 攻撃者はあなたの記憶にアクセスできますか?もしそうなら、あなたがあなたがそれを上書きする前に前に値を嗅ぐことができない/できないと思う理由はありますか?攻撃者はどのような時間枠であなたの記憶にアクセスできますか
  • 攻撃者はコアダンプにアクセスできますか?そもそもコアダンプを引き起こすほどノイズが多い代わりに、機密データにアクセスできるかどうかを気にしますか?
  • これはオープンソースですか、それともクローズドソースですか?オープンソースの場合、コンパイラwillは常に上書きされたデータなどを最適化するため、複数のコンパイラについて心配する必要があります。彼らの仕事はセキュリティを提供することではありません。 (実際の例として、SchneierのPasswordSafeには、暗号化されていないパスワードデータを保護するための特別なクラスがあります。そのために、Windows API関数を使用してメモリをロックし、強制的に上書きします彼のためにそれを行うためにコンパイラを使用するのではなく、適切に
  • これはガベージコレクションされた言語ですか?特定のガベージコレクタの特定のバージョンに、実際にデータを強制的に削除する方法を知っていますか?
  • 攻撃者が気づき、他の手段(ファイアウォールなど)で遮断する前に、攻撃者は機密データの取得を何回試みることができますか?
  • これは仮想マシンで実行されていますか?ハイパーバイザーのセキュリティをどの程度確信していますか?
  • 攻撃者は物理的にアクセスできますか?たとえば、Windowsはフラッシュドライブを使用して仮想メモリをキャッシュすることで満足しています。攻撃者が行う必要があるのは、Windowsをフラッシュドライブにプッシュするように説得することだけです。それが起こるとき、それを取り除くことは本当に難しいです。実際、非常に難しいため、フラッシュドライブからデータを確実にクリアする方法は認識されていません。

機密データの上書きを検討する前に、これらの質問に対処する必要があります。 スレッドモデルをアドレス指定せずにデータを上書きしようとするのは、誤った安心感です。

18
Cort Ammon

はい。データが不要になったときに、特に機密性の高いデータを上書きすることは、セキュリティ面で優れた方法です。つまり、オブジェクトデストラクタ(言語によって提供される明示的なデストラクタ、またはプログラムが割り当てを解除する前に実行するアクション)の一部としてオブジェクト)。それ自体が重要ではないデータを上書きすることもお勧めです。たとえば、使用されなくなったデータ構造のポインターフィールドをゼロにし、ポインターが指しているオブジェクトが解放されているときにポインターをゼロにすることも知っています。そのフィールドはもう使用しません。

これを行う1つの理由は、公開されたコアダンプ、盗まれた休止状態のイメージ、実行中のプロセスのメモリダンプを許可する侵害されたサーバーなどの外部要因によってデータが漏洩した場合です。攻撃者がRAMスティックし、データ残留を利用することは、ラップトップコンピュータおよびおそらく携帯電話などのモバイルデバイス(RAMがはんだ付けされているためにバーが高い場所)を除いて、ほとんど問題になりません。シナリオのみ。上書きされた値の残留は問題ではありません。上書きされた値の影響を受ける可能性のある微視的な電圧差を検出するためにRAMチップ内をプローブするには、非常に高価なハードウェアが必要です。 RAMへの物理的な攻撃が心配な場合、より大きな懸念は、データがCPUキャッシュだけでなくRAMに上書きされることを確認することです。しかし、繰り返しになりますが、それは通常、非常に小さな問題です。

古いデータを上書きする最も重要な理由は、悪意のある Heartbleed など、初期化されていないメモリが使用される原因となるプログラムのバグに対する防御策としてです。リスクはデータの漏洩に限定されないため、これは機密データを超えています。初期化されていないポインターフィールドが逆参照されるソフトウェアのバグがある場合、バグは悪用される可能性が低く、以下の場合追跡が容易です。このフィールドには、有効であるが意味のないメモリ位置を指す可能性がある場合よりも、すべてのビットがゼロになります。

優れたコンパイラーは、値が使用されなくなったことを検出すると、ゼロ化を最適化することに注意してください。値を使用中であるとコンパイラに信じ込ませ、ゼロ化コードを生成するために、コンパイラ固有のトリックを使用する必要がある場合があります。

自動管理を備えた多くの言語では、オブジェクトは予告なしにメモリ内で移動できます。これは、メモリマネージャー自体が未使用のメモリを消去しない限り、古くなったデータのリークを制御するのが難しいことを意味します(多くの場合、パフォーマンスのために消去されません)。プラスの側面では、高レベルの言語は初期化されていないメモリの使用を排除する傾向があるため、外部リークは通常、心配する必要があるすべてのものです(可変文字列を持つ言語の文字列作成関数に注意してください)。

ちなみに、私は上記の「ゼロアウト」を書きました。すべてゼロ以外のビットパターンを使用できます。オールゼロには、ほとんどの環境で無効なポインターであるという利点があります。あなたが知っているビットパターンは無効なポインタですが、それはより特徴的でデバッグに役立ちます。

多くのセキュリティ標準では、キーなどの機密データの消去が義務付けられています。たとえば、暗号モジュールの FIPS 140-2 規格では、最低限の保証レベルでさえ必要であり、それ以外は機能のコンプライアンスのみが必要であり、攻撃に対する耐性は必要ありません。

残りの回答に(うまくいけば)追加すると、多くの人がCでメモリを適切に上書きすることの難しさを過小評価します。「 How to zero aバッファ "。

Cでメモリを上書きする素朴な試みが直面している主な問題は、コンパイラの最適化です。最新のコンパイラのほとんどは、メモリの上書きに使用される一般的なidomが実際にプログラムの監視可能な動作を変更せず、最適化できることを認識するのに十分「スマート」です。残念ながら、これは私たちが達成したいことを完全に壊します。一般的なトリックのいくつかは、上記のリンクのブログ投稿に記載されています。さらに悪いことに、あるバージョンのコンパイラーで機能するトリックは、別のコンパイラーや同じバージョンのコンパイラーでも機能するとは限りません。バイナリのみを配布しているのでない限り、これは問題のある状況です。

私が知っているCのメモリを確実に上書きする唯一の方法は、 memset_s 関数です。悲しいことに、これはC11でのみ利用可能であるため、古いバージョンのC用に作成されたプログラムは不運です。

Memset_s関数は、cの値(unsigned charに変換)を、sが指すオブジェクトの最初のn文字のそれぞれにコピーします。 memsetとは異なり、memset_sの呼び出しは、5.1.2.3で説明されているように、抽象マシンのルールに従って厳密に評価されます。つまり、memset_sの呼び出しでは、sとnで示されるメモリが将来アクセス可能になる可能性があるため、cで示される値を含める必要があります。

確かに、Colin Percivalは、メモリを上書きするだけでは不十分だと考えています。 「 Zeroing buffers is不十分 」というタイトルのフォローアップブログ投稿から、彼は述べています

少し注意して協調コンパイラーを使用すると、バッファーをゼロにできますが、それは私たちが必要としていることではありません。機密データが保存される可能性のあるすべての場所をゼロにする必要があります。そもそも、機密情報を最初から記憶していたのは、それを使用できるようにするためでした。そして、その使用により、ほぼ確実に機密データがスタックとレジスタにコピーされました。

彼は続けて、x86プラットフォームでAESNI命令セットを使用してAES実装の例を示し、レジスターでデータをリークします。

彼はそれを主張し、

Cで前方秘密を提供する暗号システムを安全に実装することは不可能です。

確かに厄介な主張。

8
user10211

機密データが必要になった直後に上書きすることが重要です。

  • データは上書きされるまでスタックに残り、別のプロシージャからのフレームオーバーフローで表示される可能性があります。
  • データはメモリスクレイピングの対象となります。

実際、セキュリティの影響を受けやすいアプリケーションのソースコード(例 openssh )を見ると、使用後に慎重に機密データがゼロになっていることがわかります。

コンパイラが上書きを最適化しようとする可能性があることも事実であり、そうでなくても、データが物理的にどのように格納されているかを知ることが重要です(たとえば、シークレットがSSDに格納されている場合、上書きしても古いコンテンツが消去されない可能性があります ウェアレベリング )に。

4

元の例は、ネイティブのint型であるため、スタック変数を示しています。

それを上書きすることは良い考えです。それ以外の場合、他の何かによって上書きされるまでスタックに残ります。

ヒープオブジェクトまたはポインターとmallocを介して割り当てられたCネイティブ型を使用しているC++にいるかどうかは、

  1. volatileを使用します
  2. プラグマを使用して、その変数を使用するコードを囲み、最適化を無効にします。
  3. 可能であれば、シークレットは名前付き変数ではなく中間値でのみアセンブルするため、計算中にのみ存在します。

JVMまたはC#では、すべての賭けがオフになっていると思います。

3
Andy Dent

ページファイルの使用状況分布がデータを再生する方法によっては、メモリのチャンクがページアウトされて削除される前にページアウトされた可能性があります永久に

したがって、コンピュータからシークレットを正常に削除するには、まず ピン留め ページによってデータが永続メモリに到達しないことを確認する必要があります。次に、コンパイラーが削除対象のメモリーへの書き込みを最適化しないようにします。

2
ratchet freak

実際には答えはイエスです、おそらくそれを削除する前に上書きする必要があります。 Webアプリケーション開発者であれば、delete関数またはfree関数を使用する前に、すべてのデータを上書きする必要があります。

このバグを悪用する方法の例:

ユーザーは、悪意のあるデータを入力フィールドに挿入できます。データを処理してから、割り当てられたメモリのfree関数を上書きせずに呼び出すと、データがメモリに残ります。次に、ユーザーは(たとえば、非常に大きな画像をアップロードするなどして)Webアプリケーションを停止させ、UNIXシステムはコアメモリダンプをcoreまたはcore.<pid>ファイルに安全に保存します。次に、ユーザーが<pid>をブルートできる場合、それほど長くはかかりません。コアダンプファイルには、ユーザーの悪意のあるデータが含まれているため、Webシェルとして解釈されます。

1
PaulOverflow