8. 0-RTT and Anti-Replay (0-RTTと再生攻撃対策)
セクション2.3および付録E.5で述べたように、TLSは0-RTTデータに対する固有の再生保護を提供しません。懸念すべき2つの潜在的な脅威があります:
-
0-RTTデータのフライトを単純に複製することで再生攻撃を仕掛けるネットワーク攻撃者。
-
クライアントの再試行動作を利用して、サーバーがアプリケーションメッセージの複数のコピーを受信するように仕向けるネットワーク攻撃者。この脅威は、堅牢性を重視するクライアントがネットワークエラーに対してリクエストの再試行を試みることで、ある程度既に存在しています。しかし、0-RTTは、グローバルに一貫したサーバー状態を維持しないサーバーシステムに対して追加の次元を追加します。具体的には、サーバーシステムにゾーンAからのチケットがゾーンBで受け入れられない複数のゾーンがある場合、攻撃者はA向けのClientHelloと早期データをAとBの両方に複製できます。Aでは、データは0-RTTで受け入れられますが、Bではサーバーが0-RTTデータを拒否し、代わりに完全なハンドシェイクを強制します。攻撃者がAからのServerHelloをブロックすると、クライアントはBとのハンドシェイクを完了し、おそらくリクエストを再試行し、サーバーシステム全体で重複が発生します。
最初のクラスの攻撃は、0-RTTデータが最大1回受け入れられることを保証するために状態を共有することで防ぐことができます。サーバーは、このセクションで説明されている方法のいずれか、または同等の方法を実装することにより、そのレベルの再生保護を提供すべきです(SHOULD)。ただし、運用上の懸念により、すべてのデプロイメントがそのレベルで状態を維持するわけではないことは理解されています。したがって、通常の動作では、クライアントは、サーバーが実際にこれらのメカニズムのどれを実装しているか(もしあれば)を知らないため、再生しても安全であると考える早期データのみを送信しなければなりません(MUST)。
再生の直接的な影響に加えて、通常は冪等と見なされる操作でさえ、大量の再生によって悪用される可能性がある攻撃のクラスがあります(タイミング攻撃、リソース制限の枯渇など、付録E.5で説明されています)。これらは、すべての0-RTTペイロードが限られた回数のみ再生できることを保証することで軽減できます。サーバーは、そのインスタンス(マシン、スレッド、または関連するサービングインフラストラクチャ内の他のエンティティ)が同じ0-RTTハンドシェイクに対して最大1回0-RTTを受け入れることを保証しなければなりません(MUST)。これにより、再生の回数はデプロイメント内のサーバーインスタンスの数に制限されます。このような保証は、最近受信したClientHelloからのデータをローカルに記録して繰り返しを拒否するか、同じまたはより強力な保証を提供する他の方法によって達成できます。「サーバーインスタンスごとに最大1回」の保証は最低要件です。サーバーは、実行可能な場合は0-RTT再生をさらに制限すべきです(SHOULD)。
2番目のクラスの攻撃はTLS層で防ぐことができず、アプリケーションによって処理されなければなりません(MUST)。クライアントが何らかの再試行動作を実装しているアプリケーションは、既に何らかの再生攻撃対策を実装する必要があることに注意してください。
8.1. Single-Use Tickets (単回使用チケット)
再生攻撃対策の最も単純な形式は、サーバーが各セッションチケットを1回のみ使用できるようにすることです。たとえば、サーバーは、すべての未処理の有効なチケットのデータベースを維持し、使用時に各チケットをデータベースから削除できます。不明なチケットが提供された場合、サーバーは完全なハンドシェイクにフォールバックします。
チケットがデータベースキーではなく自己完結型であり、対応するPSKが使用時に削除される場合、そのPSKを使用して確立された接続は前方秘匿性を享受します。これにより、PSKが(EC)DHEなしで使用される場合のすべての0-RTTデータとPSK使用のセキュリティが向上します。
このメカニズムは、複数の分散サーバーを持つ環境でサーバーノード間でセッションデータベースを共有する必要があるため、自己暗号化チケットと比較して、PSKベースの0-RTT接続の高可用性とパフォーマンスを実現することは困難な場合があります。セッションデータベースとは異なり、セッションチケットは、一貫したストレージがなくてもPSKベースのセッション確立を正常に実行できますが、次のセクションで詳述するように、0-RTTデータの再生攻撃対策には依然として一貫したストレージが必要です。
8.2. Client Hello Recording (ClientHello記録)
再生攻撃対策の別の形式は、ClientHelloから派生した一意の値(通常はランダム値またはPSKバインダー)を記録し、重複を拒否することです。すべてのClientHelloを記録すると状態が無制限に増大しますが、サーバーは代わりに指定された時間ウィンドウ内のClientHelloを記録し、"obfuscated_ticket_age"を使用してチケットがそのウィンドウの外で再使用されないようにすることができます。
これを実装するために、ClientHelloが受信されると、サーバーはまずセクション4.2.11で説明されているようにPSKバインダーを検証します。次に、次のセクションで説明されているようにexpected_arrival_timeを計算し、それが記録ウィンドウの外にある場合は、0-RTTを拒否し、1-RTTハンドシェイクにフォールバックします。
expected_arrival_timeがウィンドウ内にある場合、サーバーは一致するClientHelloが記録されているかどうかを確認します。見つかった場合、"illegal_parameter"アラートでハンドシェイクを中止するか、PSKを受け入れますが0-RTTを拒否します。一致するClientHelloが見つからない場合、0-RTTを受け入れ、expected_arrival_timeがウィンドウ内にある限りClientHelloを保存します。サーバーは、ブルームフィルターなど、偽陽性のあるデータストアを実装することもできます(MAY)。その場合、明らかな再生に対して0-RTTを拒否して応答しなければなりませんが(MUST)、ハンドシェイクを中止してはなりません(MUST NOT)。
サーバーは、ClientHelloの検証済みセクションからのみストレージキーを派生しなければなりません(MUST)。ClientHelloに複数のPSK IDが含まれている場合、攻撃者は、サーバーがそれを検証しないと仮定して(セクション4.2.11で推奨されているように)、あまり優先されないIDに対して異なるバインダー値を持つ複数のClientHelloを作成できます。つまり、クライアントがPSK AとBを送信するが、サーバーがAを好む場合、攻撃者はAのバインダーに影響を与えることなくBのバインダーを変更できます。Bのバインダーがストレージキーの一部である場合、このClientHelloは重複として表示されず、ClientHelloが受け入れられ、再生キャッシュの汚染などの副作用が発生する可能性がありますが、異なるキーを使用するため、0-RTTデータは復号化されません。検証済みバインダーまたはClientHello.randomがストレージキーとして使用される場合、この攻撃は不可能です。
このメカニズムはすべての未処理チケットを保存する必要がないため、高い再開率と0-RTTを持つ分散システムでは実装が容易な場合がありますが、受信したClientHelloメッセージを確実に保存および取得することの難しさのため、再生攻撃対策が弱くなる可能性があります。多くのそのようなシステムでは、グローバルに一貫したストレージシステムのためにすべての受信したClientHelloを保存することは非現実的です。この場合、最良の再生攻撃対策は、単一のストレージゾーンが特定のチケットに対して権威を持ち、他のゾーンでそのチケットの0-RTTを拒否することです。このアプローチは、1つのゾーンのみが0-RTTデータを受け入れるため、攻撃者による単純な再生を防ぎます。より弱い設計は、各ゾーンに個別のストレージを実装しますが、任意のゾーンで0-RTTを許可します。このアプローチは、再生の回数をゾーンごとに1回に制限します。もちろん、どちらの設計でもアプリケーションメッセージの重複は依然として可能です。
実装が新しく起動されたとき、記録ウィンドウの一部が起動時間と重複する限り、0-RTTを拒否すべきです(SHOULD)。そうしないと、その期間中に最初に送信された再生を受け入れるリスクがあります。
注: クライアントのクロックがサーバーのクロックよりもはるかに速く動作している場合、将来のウィンドウの外にあるClientHelloが受信される可能性があり、その場合、1-RTTで受け入れられてクライアントの再試行が発生し、その後0-RTTで受け入れ可能になる可能性があります。これは、セクション8で説明されている2番目の形式の攻撃の別のバリアントです。
8.3. Freshness Checks (新鮮度チェック)
ClientHelloはクライアントがそれを送信した時刻を示すため、ClientHelloが最近再生された可能性があるかどうかを効率的に判断し、そのようなClientHelloに対してのみ0-RTTを受け入れ、そうでなければ1-RTTハンドシェイクにフォールバックすることが可能です。これは、セクション8.2で説明されているClientHelloストレージメカニズムには必要です。そうでなければ、サーバーは無制限の数のClientHelloを保存する必要があり、自己完結型の単回使用チケットにとっては有用な最適化です。これにより、0-RTTに使用できないClientHelloの効率的な拒否が可能になります。
このメカニズムを実装するために、サーバーは、セッションチケットが作成された時刻を、クライアントとサーバー間のラウンドトリップ時間のオフセット推定値とともに保存する必要があります。つまり:
adjusted_creation_time = creation_time + estimated_RTT
この値はチケットにエンコードでき、各未処理チケットの状態を保持する必要がなくなります。サーバーは、クライアントの"pre_shared_key"拡張の"obfuscated_ticket_age"パラメータからチケットの"ticket_age_add"値を減算することにより、チケットの年齢に関するクライアントのビューを決定できます。サーバーは、ClientHelloのexpected_arrival_timeを次のように決定できます:
expected_arrival_time = adjusted_creation_time + clients_ticket_age
新しいClientHelloが受信されると、expected_arrival_timeは現在のサーバーのウォールクロック時間と比較され、一定量以上異なる場合、0-RTTは拒否されますが、1-RTTハンドシェイクは完了できます。
expected_arrival_timeと測定時間の間の不一致を引き起こす可能性のあるいくつかの潜在的なエラー源があります。クライアントとサーバーのクロックレートの変動は最小限である可能性が高いですが、絶対時間は大きな値でずれている可能性があります。ネットワーク伝播遅延は、経過時間の正当な値の不一致の最も可能性の高い原因です。NewSessionTicketとClientHelloの両方のメッセージが再送信される可能性があり、したがって遅延される可能性があり、TCPによって隠される可能性があります。インターネット上のクライアントの場合、これは、クロックのエラーと測定の変動を考慮するために10秒程度のウィンドウを意味します。他のデプロイメントシナリオには異なるニーズがある可能性があります。クロックスキュー分布は対称ではないため、最適なトレードオフには、許容される不一致値の非対称範囲が含まれる場合があります。
新鮮度チェックだけでは再生を防ぐのに十分ではないことに注意してください。エラーウィンドウ中にそれらを検出しないためです。エラーウィンドウは、帯域幅とシステム容量によっては、実際の設定で数十億の再生を含む可能性があります。さらに、この新鮮度チェックはClientHelloが受信されたときにのみ実行され、後続の早期アプリケーションデータレコードが受信されたときには実行されません。早期データが受け入れられた後、レコードはより長い期間にわたってサーバーにストリーミングされ続ける可能性があります。