【ITニュース解説】Timing Attacks and Their Remedies — an in-depth guide
2025年09月13日に「Dev.to」が公開したITニュース「Timing Attacks and Their Remedies — an in-depth guide」について初心者にもわかりやすく解説しています。
ITニュース概要
タイミング攻撃は、Webサービスや組み込み機器などで、処理時間のわずかな差からパスワードなどの秘密情報を推測するサイバー攻撃だ。秘密を扱うシステムでは、この攻撃への対策が必須となる。常に一定時間で処理するコードを記述したり、専用ライブラリを利用したりして、情報漏洩を防ぐ必要がある。
ITニュース解説
タイミング攻撃とは、システムが何らかの処理を行う際に要する「時間」のわずかな違いを観察し、その情報からパスワードや暗号鍵といった秘密のデータを推測しようとする、特殊な種類の情報漏洩攻撃である。これは、システムが本来の目的とは異なる、副次的な経路(サイドチャネル)を通じて情報を漏らしてしまう攻撃の一つにあたる。多くのプログラムは、入力されるデータの内容によって処理にかかる時間が変化する。もしその入力データに秘密情報が含まれていれば、攻撃者は処理時間の変化を繰り返し測定し、推測と検証を繰り返すことで、徐々に秘密情報を突き止めることが可能になる。
タイミング攻撃は一種類の技術ではなく、さまざまな形態が存在する。例えば、プログラムが特定の条件を満たすと早期に処理を中断する「ショートサーキット」の挙動を利用するものから、CPU内部のキャッシュメモリや命令の分岐予測といったより低レベルな仕組み(マイクロアーキテクチャ)の状態変化を測るもの、さらにはネットワークを介した通信時間や、物理的な電力消費、電磁波の放出といった、間接的な情報を利用するものまで多岐にわたる。しかし、これらすべての攻撃に共通するのは、「時間」が秘密を推測するための手がかりとなる点である。
タイミング漏洩が発生する一般的な原因はいくつかある。一つは、多くのプログラミング言語で文字列やバイト列を比較する際に、最初に異なる部分が見つかった時点で比較を中止する(ショートサーキット)という挙動を利用されることだ。例えば、認証トークンをバイト単位で比較する際、先頭から何バイト一致したかによって処理時間が変化し、攻撃者はこれを手がかりに正しいトークンを特定できる可能性がある。また、秘密の値に基づいてプログラムの実行経路が変わるような条件分岐や、不正な入力に対してはすぐに処理を終了する関数なども、処理時間の差を生む原因となる。暗号処理で使われるような大きな数値を扱う計算ライブラリの中には、明示的に恒常時間処理を考慮していない場合、入力値によって処理時間が変わるものもある。さらに、CPUのキャッシュメモリや投機的実行、分岐予測といったマイクロアーキテクチャの機能も、秘密データへのアクセスパターンや処理の流れに応じて状態が変化し、その変化をタイミングとして観測することで秘密が漏洩する可能性がある。ネットワークプロトコルのパケット処理やバッファリングの仕組みも、タイミング情報の発生源となることがある。
具体的な例として、ファームウェアのハッシュ値を検証する場面を考える。プログラムがファームウェアのハッシュを計算し、期待される正しいハッシュ値とバイト単位で比較する「==」演算子を使うとする。この比較は、通常、先頭からバイトごとに一致するかを調べ、不一致のバイトが見つかった時点で比較を中断する。攻撃者がこのシステムに対して推測したハッシュ値を繰り返し送りつけ、応答時間を測定できたとしよう。もし、推測したハッシュの先頭バイトが正しいハッシュの先頭バイトと一致した場合、比較処理は次のバイトに進むため、不一致の場合よりもわずかに処理時間が長くなる。攻撃者は、すべての可能性のあるバイト値を一つずつ試すことで、最も時間が長くなるバイトを特定し、それを正しい先頭バイトとして確定させる。この手順を正しいハッシュ値のすべてのバイトに対して繰り返すことで、最終的に完全なハッシュ値を特定できるのだ。ネットワークの遅延やシステムの負荷によるノイズがあっても、統計的な手法や試行回数を増やすことで、攻撃者はこれらのノイズを克服して秘密を特定できる。
タイミング攻撃がどの程度重要になるかは、いくつかの要因に依存する。まず、漏洩させようとする情報が本当に「秘密」であるかどうかが重要だ。もしファームウェアのハッシュ値が公開情報であれば、タイミング攻撃によって漏洩しても問題にはならない。しかし、それが認証トークンや秘密鍵であれば、漏洩は致命的だ。次に、攻撃者が時間をどれだけ正確に測定できるかという点も重要になる。ローカルな環境であれば非常に正確な測定が可能だが、ネットワークを介する場合でも、多くの試行回数と統計的な分析を組み合わせることで成功する現代的な攻撃が存在する。また、攻撃者が繰り返しシステムに問い合わせできるかどうかもポイントだ。多くの問い合わせを必要とするブルートフォース攻撃に対しては、アクセス頻度制限(レートリミット)やスロットリングが有効な対策となる。
タイミング攻撃からシステムを守るための主な対策は、恒常時間コードの実装、攻撃者からの観測可能性の低減、そしてアーキテクチャレベルの制御の3つに大きく分けられる。
最も重要な対策の一つが「恒常時間(コンスタントタイム)コード」の実装である。これは、プログラムが扱うデータの「内容」に関わらず、常に同じ時間で処理を終えるようにするプログラミング手法を指す。例えば、秘密情報の比較には、データが一致するかどうかにかかわらず、常にすべてのバイトを比較し、かかる時間が変わらないような専用の関数(例:OpenSSLのCRYPTO_memcmpなど)を使用すべきである。秘密情報に基づいて条件分岐するのを避け、代わりに算術演算やビット演算を使い、どのデータが入力されても常に同じコードパスを実行するように工夫することも有効だ。暗号アルゴリズムの実装においては、恒常時間動作が検証済みのライブラリやプリミティブを利用することが不可欠である。
攻撃者の観測可能性を低減する対策としては、システムへの問い合わせ頻度を制限したり、応答時間に意図的にばらつき(ジッター)を加えることで、攻撃者が正確なタイミング情報を得にくくする方法がある。ただし、ジッターはノイズを増やすだけであり、決意の固い攻撃者は平均化によってノイズを取り除く可能性があるため、これ単独での保護は不十分だ。また、認証の成否やエラーの種類によって異なるHTTPステータスコードやエラーメッセージを返すことを避け、秘密情報が絡むチェックの結果に関わらず常に均一な応答を返すようにすることも重要である。暗号プロトコルにおいては、ブライディング技術を用いて処理をランダム化し、タイミング情報を隠す手法もある。
アーキテクチャレベルの対策としては、機密性の高い処理を、外部からの測定が困難な隔離されたハードウェア(例えば、セキュアエンクレーブ、TPM、HSMなど)上で実行する方法がある。クラウド環境では、機密性の高いワークロードと信頼できないコードが同じ物理サーバーに同居しないように配置を分離することも、マイクロアーキテクチャ攻撃を防ぐ上で有効だ。
開発者が注意すべきプログラミングパターンとしては、先述の通り、素朴な等価比較(if a == b)は多くの場合、可変時間となるため避けるべきである。代わりに、Rustのsubtle::ConstantTimeEqやOpenSSLのCRYPTO_memcmpのような、恒常時間での比較を保証するライブラリ関数を使用することが推奨される。手動で恒常時間比較を実装する場合でも、常にすべての入力バイトを処理し、途中で処理を中断しないようなロジックにすることが重要だ。また、コンパイラの最適化によって、開発者が意図しない形でコードが可変時間動作に変換されてしまうリスクもあるため、恒常時間処理のために設計されたライブラリプリミティブを利用したり、アセンブリコードを用いる必要がある場合は特に注意が必要となる。
低レベルなCPUの挙動に起因するマイクロアーキテクチャ攻撃もタイミング漏洩の一種である。キャッシュ攻撃(例: Prime+Probe, Flush+Reload)は、攻撃者がCPUのキャッシュの状態を操作し、被害者プログラムが実行された後にキャッシュの状態変化を測定することで、メモリへのアクセスパターンから秘密を推測する。投機的実行攻撃(例: Spectre)は、CPUが誤った予測に基づいて一時的に実行した命令が、マイクロアーキテクチャ上の痕跡(キャッシュの状態など)を残し、それが後からタイミング測定によって漏洩する可能性がある。これらの対策としては、秘密データに依存するメモリアクセスを避ける恒常時間アルゴリズムの利用や、CPUのマイクロコードパッチ、コンパイラによる緩和策(例: retpoline, LFENCE)などが挙げられる。
タイミング攻撃の検出と監視も重要だ。攻撃者は、多くの試行と統計的な平均化によってノイズを除去し、タイミングのわずかな差を検出する。防御側としては、rdtscのようなCPUタイマーやプラットフォーム固有のタイマーを用いて、データに依存する処理時間の変化がないかをコードレベルで詳細にベンチマーク測定することが有効だ。また、恒常時間テストフレームワークやサイドチャネル解析ツールを利用して、コードの脆弱性を自動的に検出する試みも行われている。疑わしいアクセスパターンや異常なプローブの兆候をログで監視し、検出した場合はクライアントをスロットリングしたり、警告を発したりする仕組みも必要となる。
過去の事例からも多くの教訓が得られている。ウェブアプリケーションのパスワード比較でstrcmpのような標準関数が使われ、そのタイミング情報からパスワードの一部が漏洩した事例や、HMAC(メッセージ認証コード)の検証の不備から認証鍵がリモートで復元された事例などがある。TLS(Transport Layer Security)の実装においても、可変時間な復号処理がBleichenbacherのような攻撃に利用されたことがある。これらの事例は、一見小さなコーディング上の選択が、現実世界の深刻な脆弱性につながることを示しており、機密情報を扱うコードにおいては、コードレビューの一環として恒常時間での処理が考慮されているかを常に確認する必要がある。
開発者にとっての実用的なチェックリストとしては、まず「秘密は秘密として扱う」という意識を徹底し、秘密情報が関わるあらゆるチェック処理がタイミング攻撃の対象になりうると仮定することが重要だ。恒常時間比較のためには、自分で実装せずに、検証済みのライブラリプリミティブを優先的に使用する。機密性の高い処理経路では、秘密情報に依存する条件分岐を避け、常に一定の処理経路をたどるようにする。暗号関連のコードは、継続的インテグレーション(CI)の一部として恒常時間テストを実施すべきである。認証エンドポイントなどでは、問い合わせ頻度を制限し、エラーメッセージを均一にすることで、攻撃者が手がかりを得る機会を減らす。必要であれば、暗号ブライディングのような技術を導入する。また、CPUのマイクロアーキテクチャ攻撃に対する緩和策をプラットフォーム全体で適用し、可能であれば機密性の高い計算はハードウェア的に隔離された環境(HSM、セキュアエンクレーブ)で行うことも検討する。そして、常に不正なプローブパターンを監視し、異常を検出した際には適切な対応を取ることが不可欠だ。
恒常時間コードの実装は、素朴な実装と比較してわずかに処理が遅くなったり、コードが複雑になったりする可能性がある。しかし、通常、その性能コストは得られるセキュリティ上の利益に比べてごくわずかである。ジッターの追加やスロットリングといった緩和策は、攻撃コストを増加させるが、単独で完全な保護を提供するものではない。多くのシステムにおいて、恒常時間コードの利用、レート制限、監査、ハードウェアによる隔離といった多層的なアプローチが最も効果的である。まずは秘密情報を扱う部分を特定し、可変時間で動作する可能性のあるプリミティブを恒常時間のものに置き換え、検出と監視の仕組みを追加する。そして、あらゆる機密情報を扱う機能のセキュリティレビューにおいて、タイミング分析を常に考慮に入れるべきである。