変数に格納された機密データが削除される(またはスコープから外れる)前に、機密データを上書きすることは、安全なプログラミング手法ですか?私の考えでは、ハッカーが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日ブログエントリをお読みください。または今週の質問を送信してください。
はい、上書きしてから、値を削除/解放することをお勧めします。 各言語が相互作用するため、実行する必要があるのは「データを上書きする」こと、またはGCが処理する範囲から外れることを前提としないハードウェアとは異なります。
変数を保護する場合、次のことを考慮する必要があります。
「消去」の実際の実装は、言語とプラットフォームによって異なります。使用している言語を調査し、 安全にコーディングできるかどうかを確認してください 。
なぜこれが良い考えなのですか?クラッシュダンプ、およびヒープを含むすべてのものに機密データが含まれる可能性があります。インメモリデータを保護する場合は、次の使用を検討してください
言語ごとの実装ガイドについては、StackOverflowを参照してください。
ベンダーガイダンス(この場合はMSFT)を使用する場合でも、 SecureStringの内容をダンプすることは引き続き可能です 、および 高セキュリティシナリオ用の特定の使用ガイドラインがある場合があります。
再度使用されない値を格納しますか?それが提供するかもしれないどんな利点にも関係なく、最適化される何かのように見えます。
また、言語自体の動作方法によっては、実際にメモリ内のデータを上書きできない場合があります。たとえば、ガベージコレクターを使用している言語では、すぐには削除されません(これは、他の参照がぶら下がっていないことを前提としています)。
たとえば、C#では、以下は機能しないと思います。
string secret = "my secret data";
...lots of work...
string secret = "blahblahblah";
"my secret data"
は不変であるため、ガベージコレクションが行われるまで待機します。その最後の行は、実際には新しい文字列を作成しており、その秘密のポイントを持っています。実際の秘密データが削除される速度は速くなりません。
メリットはありますか?データを上書きしていることを確認できるようにアセンブリまたは何らかの低レバー言語で記述し、アプリケーションを実行したままコンピュータをスリープ状態にするか、そのままにしておくと、RAM =邪悪なメイドによってこすり取られ、邪悪なメイドは私たちのRAMシークレットが上書きされた後、それが削除される前のデータ(非常に小さなスペースの可能性があります)を取得しました。 RAMまたはハードドライブ上でこの秘密を解き放ちます...それから私はセキュリティの可能性のある増加を見ます。
しかし、コストと利益の関係で、このセキュリティ最適化は、最適化のリストで非常に低くなっているようです(そして、ほとんどのアプリケーションで一般に「価値がある」というポイントを下回っています)。
可能な限り最短の時間でそれを確実に保持するために短期間に秘密を保持することを意図した特別なチップでこれの使用が制限されるのを見る可能性がありますが、それでも私はコストの利益については不確かです。
どのような種類のハッキングを防止しようとしているのかを説明する脅威モデルが得られるまで、セキュリティ変数の上書きについて考え始めるべきではありません。 セキュリティには常にコストがかかります。この場合、コストは、データを保護するためにこのすべての追加コードを維持することを開発者に教える開発コストです。 このコストは、開発者がミスを犯す可能性が高く、それらのミスがメモリの問題よりもリークの原因である可能性が高いことを意味します。
機密データの上書きを検討する前に、これらの質問に対処する必要があります。 スレッドモデルをアドレス指定せずにデータを上書きしようとするのは、誤った安心感です。
はい。データが不要になったときに、特に機密性の高いデータを上書きすることは、セキュリティ面で優れた方法です。つまり、オブジェクトデストラクタ(言語によって提供される明示的なデストラクタ、またはプログラムが割り当てを解除する前に実行するアクション)の一部としてオブジェクト)。それ自体が重要ではないデータを上書きすることもお勧めです。たとえば、使用されなくなったデータ構造のポインターフィールドをゼロにし、ポインターが指しているオブジェクトが解放されているときにポインターをゼロにすることも知っています。そのフィールドはもう使用しません。
これを行う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で前方秘密を提供する暗号システムを安全に実装することは不可能です。
確かに厄介な主張。
機密データが必要になった直後に上書きすることが重要です。
実際、セキュリティの影響を受けやすいアプリケーションのソースコード(例 openssh )を見ると、使用後に慎重に機密データがゼロになっていることがわかります。
コンパイラが上書きを最適化しようとする可能性があることも事実であり、そうでなくても、データが物理的にどのように格納されているかを知ることが重要です(たとえば、シークレットがSSDに格納されている場合、上書きしても古いコンテンツが消去されない可能性があります ウェアレベリング )に。
元の例は、ネイティブのint型であるため、スタック変数を示しています。
それを上書きすることは良い考えです。それ以外の場合、他の何かによって上書きされるまでスタックに残ります。
ヒープオブジェクトまたはポインターとmallocを介して割り当てられたCネイティブ型を使用しているC++にいるかどうかは、
JVMまたはC#では、すべての賭けがオフになっていると思います。
ページファイルの使用状況分布がデータを再生する方法によっては、メモリのチャンクがページアウトされて削除される前にページアウトされた可能性があります永久に。
したがって、コンピュータからシークレットを正常に削除するには、まず ピン留め ページによってデータが永続メモリに到達しないことを確認する必要があります。次に、コンパイラーが削除対象のメモリーへの書き込みを最適化しないようにします。
実際には答えはイエスです、おそらくそれを削除する前に上書きする必要があります。 Webアプリケーション開発者であれば、delete
関数またはfree
関数を使用する前に、すべてのデータを上書きする必要があります。
このバグを悪用する方法の例:
ユーザーは、悪意のあるデータを入力フィールドに挿入できます。データを処理してから、割り当てられたメモリのfree
関数を上書きせずに呼び出すと、データがメモリに残ります。次に、ユーザーは(たとえば、非常に大きな画像をアップロードするなどして)Webアプリケーションを停止させ、UNIXシステムはコアメモリダンプをcore
またはcore.<pid>
ファイルに安全に保存します。次に、ユーザーが<pid>
をブルートできる場合、それほど長くはかかりません。コアダンプファイルには、ユーザーの悪意のあるデータが含まれているため、Webシェルとして解釈されます。