this の素晴らしい答えを読んだ後、サイドチャネル攻撃の存在について学びました。
提供されたコード例から、さまざまな入力が与えられたときにコードをタイミング調整することにより、正しいパスワードを決定することが可能です。
for (i = 0; i < n; i++) {
if (password[i] != input[i]) {
return EFAIL;
}
}
コードがそのようなタイミング攻撃に対して脆弱でないことを確認するにはどうすればよいですか?さまざまな一般的なソフトウェア構成の例とベストプラクティスを回答で提供できるように、わざとこの制限を設けません。
攻撃者が探す可能性のあるサイドチャネルが多数あるため、サイドチャネル攻撃の検出は悪名高い。これには以下が含まれますが、これらに限定されません。
Wikipedia には優れたリストがあり、これは単なる抜粋です。非常に多くの異なるサイドチャネルがあるため、それらのそれぞれに個別に対処する必要があります。
あなたのコードはタイミング攻撃に対して脆弱ですが、あなたはすでにそれを知っていました。問題は、どうすれば修正できるでしょうか。解決策は、一定時間の比較を行うことです。 1つの例は、次のようなコードです。
difference = 0;
for (i = 0; i < n; i++) {
difference |= (password[i] ^ input[i]);
}
return difference == 0 ? E_OK : E_FAIL;
このコードは、パスワードと入力の長さが同じであることを前提としています。ハッシュ関数の出力だからです。このコードは、要素の各ペア間のビット差を累積し、差がゼロの場合に基づいて結果を返します。また、親しみやすい最適化Cコンパイラは、これが何をしているかを特定し、元の(壊れた)コードに対して生成するアセンブリを生成する自由があることに注意してください。実際の生成アセンブラーを確認する必要があります(またはこのために設計されたライブラリー関数を使用します)。
もちろん、これは1種類のサイドチャネル攻撃からのみ保護し、他の種類の攻撃からは保護しません。
それは完全にあなたが焦点を当てているサイドチャネルに依存します。電力消費などの一部は物理的なアクセス(または消費を測定する他の方法)を必要とするため、攻撃者が遠く離れている場合は問題にならない場合があります。
一般に、サイドチャネル攻撃から防御するには、次のことが必要です。
実際のシステムでは プレーンテキストでパスワードを保存することは決してない であるため、質問のコードは説明のための意図的に単純化された例にすぎないと思います。ただし、この架空のコードをタイミング攻撃に対して脆弱ではない実装に置き換えたい場合は、アルゴリズムが最初の間違った文字で終了せず、常に同じ数の比較を行うようにします。
bool isCorrect = true;
for (i = 0; i < PASSWORD_MAX_LENGTH; i++) {
if (password[i] != input[i]) {
isCorrect = false;
}
}
return isCorrect;
ただし、CPUがこのコードを処理する方法によっては、失敗した場合により長くまたは短くなる可能性があるため、これもタイミング攻撃に対する完全な証拠ではありません。タイミングの違いの1つの考えられる原因は、 分岐予測 です。
大幅に単純化し過ぎた:CPUがforループでif条件を処理し、そのif条件がほとんどの場合falseとなることに気づくと、CPUは常にfalseとなるという仮定に基づいて最適化します。これにより、そのforループをはるかに高速に処理できます。しかし、もしifステートメントが突然trueになった場合、CPUパイプライン内でかなりの混乱が生じ、クリーンアップに数クロックサイクルかかります。したがって、分岐予測の失敗によって引き起こされるタイミングの違いは、別の可能なタイミングサイドチャネルになる可能性があります。これは、開発者にとって完全に不透明であり、CPUの正確なモデルに依存することさえあるCPUの機能であるため、回避するのは困難です。詳細については、 Spectreの脆弱性 について調査してください。
しかし、タイミング攻撃を回避するための別のアプローチもあり、粗雑でシンプルですが効果的です:各パスワード比較の後にランダムな遅延を追加します。遅延の長さが 暗号学的に安全な疑似乱数ジェネレータ に由来する場合、攻撃者が依存する時間測定の精度が損なわれます。
ここでは、サイドチャネル攻撃を時間ベースの攻撃、つまり、.
タイミング攻撃は、暗号化システムまたはアルゴリズムを実行しているハードウェアのCPUまたはメモリに出入りするデータの動きを監視します。暗号化操作の実行にかかる時間の変化を観察するだけで、秘密鍵全体を特定できる可能性があります。このような攻撃には、タイミング測定の統計分析が含まれ、ネットワーク全体で実証されています
入力をストリームとしてバイトごとにチェックし、ユーザーが出力が正しいかどうかを確認できるコントローラー/画面/ UIに応答する代わりに、データをブロックとして使用し、入力データに対して同等の算術演算を実行する必要があります。
この攻撃は、排除できる出力の統計分析を利用します。このような操作を実行する1つの方法は、パスワードの長さがどれほど長くてもかまいません、常に固定長の出力を生成するハッシュを使用することです。
免責事項:私はこの分野の初心者です。
予想される期間をチェックコードに設定して、少なくともその期間は実行を継続させてみませんか?
DateTime endTime = DateTime.Now + TimeSpan.FromMilliseconds(10);
while (DateTime.Now < EndTime || passwordCheck.IsIncomplete) {
// password checking code here
}