Java共有シークレットを使用してクライアントの身元を確認するWebアプリケーションがあるとします。シークレットはサーバーに保存され、クライアントはSSLを介してシークレットを送信し、チェックされます。
_String SECRET_ON_SERVER = "SomeLongRandomValue";
if (secretFromClient.equals(SECRET_ON_SERVER)) {
// request verified - client knows the secret
} else {
// request not verified - generate failed response
}
_
String.equals(String)
は、単一の文字が一致しないとすぐに戻ります。これは、攻撃者が応答にかかる時間を正確に追跡できれば、理論的にはその試行の文字数-secretFromClient
-がサーバーシークレットと一致することを知っているはずであり、ブルートフォース攻撃が行われる可能性があります。
しかしタイミングの違いはtinyのようです。迅速な調査は、違いが簡単にサブミリ秒の範囲にあることを示唆しています。
理論的には、これはエクスプロイトの可能性があります。スーパーパラノイアモードの場合は、「はい」と答えてください。他のすべてのケースでは、答えは"No。"になります。
彼らが成功したタイミング攻撃を実行することができると主張する公開された論文(1つは回答で@Oasiscircleにリンクされています)がありますが、前提条件も注意深く読む必要があります。これらの公開された「実用的な」攻撃はいくつかのアルゴリズムLAN上で動作し、その間にスイッチが1つ、多くても2つあります。これは、ほぼ完全に信頼できる一定の往復時間を意味します。そのシナリオでは、タイミングを介して特定のアルゴリズムを攻撃することは実際に現実的ですが、これは質問のコンテキストでは意味がありません。
実際、私はこれらのリモート攻撃を"不正行為"と見なしています。実験を注意深く設計して遅延がほとんど正確に予測可能である場合、攻撃がリモートであるという事実は重要ではありません。
インターネット上のanyサーバーを攻撃する場合、地理的およびトポロジー的に近いサーバー上であっても、この前提条件は保持されません(リモートであってもpun意図的)。
また、タイミングによる文字列比較への攻撃は、RSA計算への攻撃とはまったく異なります。操作全体と測定可能な差がはるかに小さいため、これははるかに困難です。
パスワードの文字列比較(パスワードが「妥当な」サイズであると想定)は数百サイクル以下かかります。このうち、可能な最初のキャッシュ/ TLBミスが最大の主な要因であり、その後に端末の誤予測ブランチ(一致と非一致の両方で発生します)。一致と非一致の違いは、おそらく1〜20ナノ秒です。
キャッシュミスと同様に、コンテキストの切り替えには数百ナノ秒かかります。スケジューラは通常、マイクロ秒またはミリ秒の分解能で動作し、控えめに言っても予測が困難な時間の間に非常に重要な作業(数百/数千ナノ秒)を実行します。
ナノ秒スケールで確実に差異を測定するまったくも、完全に簡単なことではありません。通常のプログラム可能なタイマーには、必要な分解能がほとんどありません。コモディティハードウェアのHPETは、100ナノ秒の分解能(仕様ごと)を提供することが保証されており、実際には多くの実装で1ナノ秒に低下します。ただし、割り込みを生成することで機能します。これは、ナノ秒まで正確な特定の時点にタイマーをスケジュールすることはできますが、実際にはそれをメジャー単一ナノ秒に使用することはできないことを意味します。また、割り込みによってオーバーヘッドと数十ナノ秒の不確実性が追加されます(...から数十ナノ秒を測定したい!)。正確なサイクルカウンターをシリアル化する必要があります。また、その正確さはパイプラインの外観に依存するため、ナノ秒の解像度で外部イベントを正確に測定するのにかなり役に立たなくなります。
正当なユーザー(そうです、それらも存在します!)などの予測不能なノイズを追加し、合体を中断するなど、考慮すべき事項が他にもあります。
いくつかのsomething-different-nanoとsomething-microおよびいくつかのsomething-milliを含むサンプルからsomething-nanoを神聖化しようとしています_は非常に難しいタスクです。それは、あらゆる規模のいくつかの独立したソースからのノイズです。
最後に、「Java」についての言及を検討してください。ガベージコレクターが予測できない時間に実行されている可能性があり(いずれの場合も、リモートの攻撃者にとって予測不可能)、未知の(マイクロ、ミリ?)スケールで予測できないジッターを引き起こします。
理論的には、もちろん、マイクロ秒スケールなどの低解像度でも多数のサンプルを収集し、さまざまなノイズ源を統計的に排除することができます。パスワードが正しいかどうかを確実に判別することはできませんが、最終的には十分に高い確率(85%または90%、または99%など)で判別できるため、手動で確認することができます。少数の候補者。それで十分です!
これは、少なくとも理論的には可能ですが、単一のパスワードを使用する場合でも巨大個のサンプルが必要になります。そして、「巨大な」と言うことは、実際には銀河の割合の控えめな表現です。実際に必要なサンプル数は、攻撃を並列化する必要があることを意味します。そうしないと、時間がかかります。
現在、このようなタイミングアタックを深刻な程度に並列化することは、(量子力学と同じ意味で)オブザーバー効果の影響を受けるため、簡単には不可能です。
サーバーに十分なアイドルコアがあると仮定して、いくつかのプローブ(多分5〜8)を並行して実行すると、正常に機能するはずですが、スケールアップすると、最終的に1つのプローブが予測不可能な不均衡な状態で別のプローブの結果に影響を与えます仕方。それが起こらないようにするためにあなたができることは何もないので、並列化は実際にはうまくいきません(私は通常、割り込みが単一のコアを通過し、データを送信する物理的な銅線が1本しかないという事実さえ考慮していません通過する必要があるため、サーバーにまだアイドルコアが残っている場合でも、1つのプローブが別のプローブに影響を与える可能性がありますmay。
一方、非大規模並列攻撃を実行すると、単一のパスワードを見つける前に古い年齢で死ぬため、失敗する可能性があります。
リモートタイミング攻撃の成功例が公開されています 。ドキュメントから- "...リモートのタイミングの違いを20µsまで確実に区別できます。"はい、そのため、 .equals()
の実装(ネタバレ:安全ではありません)。 XOR)の合計を使用して.equals()
を実装し、タイミングに依存しない方法で比較します。
以下は、バイトのタイミングに依存しない比較の例としてのpython実装です。
def equals(bytes1, bytes2):
if len(bytes1) != len(bytes2):
return False
else:
differences = 0
for a, b in Zip(bytes1, bytes2):
differences |= a ^ b
return differences == 0
シークレットの適切な暗号化ハッシュをサーバーに保存します(つまり、パスワードのように扱います)。次に、クライアントが送信した文字列のハッシュを取得し、ハッシュを比較します。
シークレットに十分なエントロピーがある場合、これはタイミング攻撃およびを排除し、実際のシークレット文字列の漏洩を防止します。ハッシュから。
一方、秘密のエントロピーの量が辞書攻撃を防ぐのに十分でない場合、これだけでは十分ではありません。早期終了の比較では、攻撃者がハッシュの最初の数バイトを知ることができます。その後の辞書攻撃では、ハッシュから秘密を回復できる可能性があります。 (このようなタイミング攻撃の可能性の詳細については、 パスワードハッシュに対するタイミング攻撃 も参照してください。)これは、一定時間の比較方法を使用して2つのハッシュを比較することで防ぐことができます。
したがって、最も堅牢なソリューションは、シークレットのハッシュを保存し、クライアントが送信する文字列をハッシュし、安全な一定時間比較メソッドを使用して2つのハッシュを比較することです。ソルトハッシュを使用しても害はありません。
キーではなくハッシュを比較する@Benの答えは、タスクの最適な方法アプローチであり、en passantも問題の部分的なソリューションになります。
ただし、Rainbowのテーブル化されたハッシュツリーの特定のレベルに対して脆弱なままです。各文字で始まるハッシュを生成するキーを試し、見つかった文字で始まり、2番目の文字を循環するキーなどを試します。塩とコショウはおそらくこれに対処する方法をとりますが、それがタイミングの問題である場合、問題のより良い解決策は、100msの次の倍数(または何でも)まで比較後にスリープすることにより、タイミング攻撃を完全に防ぐことです時間はstrcmpの場合よりも明らかに大きくなります)。
質問に答えるためには、リスクの大きさを計算する必要があります。
1GHzマシンでは、クロックサイクルはナノ秒です。文字列比較の単一文字は、CPUキャッシュヒットと同様に、この順序である必要があります(L1の場合は〜1nsからL3の場合は〜30ns)。私が理解しているように、ほとんどの文字列比較関数は、メモリから文字を1つずつ取得するのではなく、最も効率的なトレードオフの一括サイズで文字を取得します。たとえそうであったとしても、DRAMアクセスを必要とするキャッシュミスは、最悪の場合でも、わずか約100ns /文字です。
@Oasiscircleは、WAN 20usのタイミングを引用して、記事にリンクしています。
ただし、マシンがホストされている場所を確立するのは簡単です。同じNOCまたはサーバーファーム内のマシンをレンタルしている誰かが、正確なハードウェア構成を知っており、おそらく1つまたは2つのスイッチしかありません。 2台のサーバーをレンタルする場合は、テストシステムをセットアップして、そのNOCを介して測定時間をテストし、結果をできるだけ近づけることができます。
したがって、その論文からおそらくより興味深いのは、エラーレートが5%未満の2,000回の測定で100nsのLANタイミングです(または1,000回の測定で200ns:より多くの測定でさらに優れた解像度が得られます)。彼らはネットワークハードウェアについては説明しませんでしたが、攻撃マシンとターゲットマシンの両方にギガビットカードがある場合、解像度がさらに向上する可能性があります。
その時点で、攻撃者は最も近い1から100文字の間のどこかに長さを区別することができます(キャッシュヒットがうまくいくか、テストがいくつ実行されるか、ネットワークの速度、およびハードウェアの細かい点が多すぎるかによって異なります) )。
キャッシュミスを保証する攻撃があり、任意の数のテストを実行できること、攻撃に最適なハードウェアを選択すること、攻撃の解決策は、アプリケーションとサーバーの寿命...これにより、効果的に1文字の解像度、つまりキー全体が得られます.
したがって、システムに技術的に高度な攻撃者がマシンをターゲットにすることにお金を費やす可能性が高い場合は、このアプローチでほぼ確実に成功するため、これは明らかなリスクです。
同様に、NOCでスペースを借りている場合、少なくともこの比較がドライブバイ攻撃で発見される可能性のあるプロトコルを使用して行われている場合は、他のNOC居住者からのドライブバイ攻撃でもリスクがあります。
短い答え:はい、リスクです。操作の時間がstrcmpの影響を受けるのを防ぐのは簡単です。それを行うのが最善です。
攻撃の実用性ではなく、安全に物事を行う習慣についてです。 「文字列は数字だけなので、検証を無視することができる」ため、誰かがその文字列をエスケープするかどうかを考えたため、バグが侵入するのはあまりにも一般的です。次に誰かがそれを悪用する方法を見つけます。
暗号で保護された文字列比較を使用するのは非常に簡単で安価なので、本当に必要な場合と必要でない場合について考える理由はありません。パスワード、認証トークンなどを扱う場合は常に、一定時間の文字列比較アルゴリズムを使用します。
if a.length != b.length return false
x = 0
for i = 0; i < a.length; i++ {
x |= a[i]^b[i]
}
return x == 0
あなたは間違った質問をしています。心配は感情であり、感情は私たちの心に脅威を警告するはずですが、警告されたら、脅威とは何かについてもっと深く考える必要があります。多くの場合、1つのリスクを他のリスクと比較して測定すると便利です。 equalsメソッドへのタイミング攻撃は現実的であり、潜在的に悪用可能ですが、対処する脅威がはるかに大きい場合があります。質問はこれに対するあなたの応答がどうあるべきかになります。それはあなたの文脈に大きく依存します。
ここで大量の応答を読むと、膨大な量の計算、仮定、緩和要因が表示されます。最終結果は、これは引き離すのがそれほど簡単な悪用ではないということです。
最終的にセキュリティとは、リスクとコストのバランスをとることです。私たちはすべての問題に対処するための無限のリソースを持っていることは決してないので、私たちは常に対処しなければならないものを選んで選択することを余儀なくされています。
これは実際に悪用される可能性があることを開発者に通知し、文字列の比較は直接ではなく安全なアルゴリズムを使用して行う必要があります。ここでのコストは、新しいコードでは本当に最小限であり、リスクの可能性を排除します。
既存のコードについて、緩和する状況がない場合は、問題の修正を検討してください。たとえば、これらの攻撃は数千回の試行で数千回を必要とします。エクスプロイトがパスワードベースであり、試行を3回だけに制限すると、このエクスプロイトで何か(ハッシュとしましょう)を明らかにすることはほぼ不可能になります。一方、レート制限のない高価値ターゲットに対するセキュリティメカニズムがある場合は、このコードの修正を検討する必要があります。
<1msのタイミング攻撃が成功した例はありますかインターネット経由
あなたが考えたいのは、誰かが秘密を内部で発見しようとしたかどうかです。タイミングは、内部の観点からより正確なレベルで判断できます。このような状況が心配な場合は、最初の正しくない文字で終了するのではなく、送信されたすべての文字を評価するように関数を変更してみませんか?そうすれば、タイミングは均一になります。
文脈から判断することはできません。
受け入れられた回答に記載されているように、このような1つの文字列比較は攻撃するのが困難です。タイムラインは単に短すぎます。ただし、コンテキストは不可欠です。巧妙なハッカーがこのブロックの周りの残りのコードを成形してタイミングを増幅できれば、どこかに到達できる可能性があります。
たとえば、ハッカーがクライアントに何かを送信する前にこの関数を10,000,000回コールバックする巧妙な方法があった場合、問題が発生する可能性があります。突然、ミリ秒未満の範囲の問題が数秒の問題になり、ネットワークノイズに対してはっきりと見えるようになりました。
これは、一定時間比較に切り替えることをお勧めするケースですif脅威モデルにはタイミング攻撃が含まれます。あなたはcould座ってコードベース全体を分析し、攻撃者がそのような巧妙な繰り返しのトリックを引っ張ることができないことを証明し、その後、品質分析ステップを組み込んで、将来の変更がこの分析を妨害しないようにします。または、一定時間の比較に切り替えることもできます。 「一定時間の比較はおそらくやり過ぎですが、プログラム全体を分析してこのコードを誤用しないようにするよりも、コードのこの小さなセクションをタイミング攻撃に対して安全にする方が簡単でした。タイミング攻撃につながる可能性があります。」
マイナーコードの可読性の問題がいくつかあり、比較が実際に機能することとString.equalsが機能することを証明するために、QAステップを実行する必要がある場合があります。パフォーマンスコストはおそらくないでしょう。ほとんどのユーザーが正当で秘密を持っている場合は、とにかく文字列全体を比較する必要があります。