Error Events

CORB Leaks

October 1, 2020
Abuse Browser Feature, Error Events, Content-Type, nosniff
Category Attack
Defenses Fetch Metadata, SameSite Cookies

Cross-Origin Read Blocking (CORB) は、Spectre などの投機的サイドチャネル攻撃の影響を軽減することを目的とした、Web プラットフォームのセキュリティ機能です。残念ながら、特定のタイプのリクエストをブロックすることで、あるリクエストではCORBが実行され、別のリクエストでは実行されなかったことを攻撃者が検出できる、新しいタイプのXS-Leaks 1 をもたらしました。とはいえ、このXS-Leaksは、CORBによって積極的に保護される問題(Spectreなど)よりもはるかに影響は少ないです。 info これはChromiumの既知の問題であり、未修正のままであるかもしれませんが、ChromiumベースのブラウザでデフォルトでSameSite Cookieが展開されることにより、その影響は大きく軽減されます。 CORB & Error Events # 攻撃者は、CORBがレスポンスからボディとヘッダーを取り除く結果となるステータス コード2xxで、レスポンスがCORB protectedContent-Type(およびnosniff)を返す場合、CORBの保護機能が強制的に実行されたことを観察することができます。この保護機能を検出すると、攻撃者はステータスコード (成功 もしくは エラー) と Content-Type (CORBで保護されているかどうか) の両方の組み合わせをリークさせることができます。これにより、以下の例に示すように、2つの可能な状態を区別することができます。 1番目の状態はリクエストがCORBによって保護され、2番目の状態では、クライアントエラー(404)となる。 1番目の状態はCORBによって保護され、2番目の状態では保護されない。 以下の手順で、最初の例の文脈でこの保護機能を悪用することができます。 攻撃者は、Content-Typeがtext/htmlでnosniff ヘッダーが設定された200 OKのレスポンスを返すリソースをscriptタグに、クロスオリジンリソースとして埋め込むこみます。 機密性の高いコンテンツが攻撃者のプロセスに入るのを防ぐため、CORBは元のレスポンスを空のレスポンスに置き換えます。 空のレスポンスは有効なJavaScriptであるため、onerror イベントは発生せず、onload が代わりに発生します。 攻撃者は、1.と同様に2番目のリクエスト(2番目の状態に対応)をトリガーし、200 OK 以外のものを返します。このとき、onerror イベントが発生します。 興味深い動作は、CORBがリクエストから有効なリソースを作成し、JavaScript以外を含む可能性がある(エラーを引き起こす)ことです。非CORB環境を考慮すると、1.と 4.の両方のリクエストがエラーを引き起こします。これは、これらの状況によって区別可能であるとしてXS-Leakを導入しています。 nosniffヘッダーの検出 # CORBは、リクエストに nosniff ヘッダーが存在する場合、攻撃者に検出されてしまう可能性があります。この問題は、CORBがこのヘッダーの存在と一部のスニッフィングアルゴリズムによってのみ強制的に実行されることに起因しています。以下の例では、2つの区別可能な状態を示しています。 CORBは、リソースがnosniffヘッダーと共にContent-Typeがtext/htmlで提供される場合、scriptとして認識されたリソースを埋め込んだ攻撃者ページを防止します。 リソースがnosniffを設定せず、CORBがページの Content-Typeを推測できない場合(text/htmlのまま)、コンテンツが有効なJavaScriptとして解析できないためSyntaxErrorが発生します。このエラーは、scriptタグが特定の条件下でのみエラーイベントをトリガーするため、window.onerror をリッスンすることで捕捉できます。 対策 # SameSite Cookies (Lax) COOP Framing Protections Isolation Policies ✔️ ❌ ❌ RIP 🔗 NIP 🔗 – 異なるシナリオに対して有効な防御機構を組み合わせる必要があります。 ...

Error Events

October 1, 2020
Abuse Error Events, Status Code, nosniff, Content-Type
Category Attack
Defenses Fetch Metadata, SameSite Cookies

Webページがサーバにリクエストを発行すると(例:フェッチ、HTMLタグ)、サーバはこのリクエストを受信して処理します。 受信した際にサーバは与えられた条件に基づいて、リクエストが成功(例:200)すべきか失敗(例:404)すべきかを決定します。 レスポンスがエラーステータスを持つ場合、ページを処理するためにブラウザによってerror eventが発行されます。 これらのエラーは、例えばHTMLコンテンツを画像として埋め込もうとした時のようなパーサーの処理が失敗するケースにも対応しています。 たとえば、攻撃者は認証されたユーザだけが利用できるリソースにユーザがアクセスできるかどうかを確認することで、ユーザがサービスにログインしているかどうかを検出できます1。 このXS-Leakの影響はアプリケーションによって異なりますが、ユーザを非匿名化する高度な攻撃につながる可能性があります2。 エラーイベントは様々な HTMLタグからスローされる可能性があり、ブラウザによって動作が異なるものもあります 3。 例えば、読み込まれたリソースやHTMLタグ、特定のヘッダ(例えば nosniff や Content-Type )の存在、あるいはブラウザのデフォルトの保護機能の適用などによって動作が変わることがあります。 エラーイベントで情報をリークさせる原理は、さまざまなXS-Leakに抽象化して適用できます。 たとえば、Cache Probingの1つの手法では、特定の画像がブラウザによってキャッシュされたかどうかの検出にエラーイベントを使用しています。 コード # 以下のコードは、<script> タグを使用してエラーイベントを検出する方法を示しています。 function probeError(url) { let script = document.createElement('script'); script.src = url; script.onload = () => console.log('Onload event triggered'); script.onerror = () => console.log('Error event triggered'); document.head.appendChild(script); } // google.com/404 が HTTP 404 を返すため、スクリプトはエラーイベントを発生する probeError('https://google.com/404'); // google.com は HTTP 200 を返すので、スクリプトは onloadイベントをトリガーする probeError('https://google.com/'); 対策 # このXS-Leakの緩和策は、アプリケーションが特定のリソースをどのように処理するかによって異なる可能性があります。 一般的なアプローチとしては、可能な限り一貫した挙動を採用することです。 特定のシナリオでは、アプリケーションはSubresource Protectionsを使用して、攻撃者がURLを予測して攻撃を進行させることを防ぐことができます。 最後に、一般的なWebプラットフォームのセキュリティ機能を導入することで、アプリケーションの実装に大きな変更を加えることなく、このXS-Leakをより大幅に緩和することができます。 ...

Cache Probing

Abuse window.open, Error Events, Cache, iframes, AbortController
Category Attack
Defenses SameSite Cookies, Vary: Sec-Fetch-Site, Subresource Protections

Cache Probingは、あるリソースがブラウザによってキャッシュされているかどうかを検知する手法です。そのコンセプトは、初期のWeb1から知られており、当初はタイミングの差異を検知することに基づいていました。 ユーザがWebサイトに訪れると、画像、スクリプト、そしてHTMLコンテンツなどの様々なリソースが取得され、それらは(特定の条件下で)ブラウザによってキャッシュされます。この最適化により、ブラウザはこれらのリソースを再度要求することなくディスクから提供するため、その後のナビゲーションが高速化されます。もし攻撃者がどのリソースがキャッシュされているかを検知できれば、この情報を基に、ユーザが特定のページにアクセスしたことがあるかどうかをリークできます。 Cache Probingのバリエーションとして、Error Eventsを悪用することにより、より正確でインパクトのある攻撃を実行することができます。 攻撃の原理 # 攻撃者は、あるユーザーがあるSNSを訪問したかどうかを知りたいと考えています。 そのSNSにアクセスすると、いくつかのサブリソースがキャッシュされます。 ユーザが、SNSが通常取得するリソースを取得する攻撃者のページを訪問する。 Network Timing XS-Leakの手法を用いて、攻撃者のページはキャッシュからの応答(つまりステップ1が起こった)とネットワークからの応答(つまりステップ1が起こらなかった)の違いを検出することができます。(応答がキャッシュからの場合、遅延は顕著に短くなります。) Error EventsによるCache Probing # Error Events2を利用したキャッシュプロービングは、より正確な攻撃を可能にします。このアプローチでは、時間の計測に頼らず、Error Eventsといくつかのサーバサイドの挙動を活用して、あるリソースがキャッシュされたかどうかを検出します。この攻撃は、以下のステップを必要とします。 ブラウザキャッシュからのリソースを無効化する。このステップは、攻撃が別の訪問で以前にキャッシュされたリソースを考慮しないことを確認するために必要です。 ユーザーの状態によって異なる項目がキャッシュされるようなリクエストを実行する。例えば、ユーザーがログインしている場合にのみ、特定の画像を含むページをロードする。このリクエストは、<link rel=prerender...で対象のウェブサイトに移動したり、iframeでウェブサイトを埋め込んだり、window.openで新しいウィンドウを開くことで発生させることができる。 サーバが拒否するようなリクエストを引き起こす。例えば、長大なRefererヘッダを含んでいて、サーバーがリクエストを拒否するような場合です。もし、ステップ2でリソースがキャッシュされていれば、このリクエストはエラーイベントを発生させることなく、成功します。 キャッシュの無効化 # キャッシュからのリソースを無効にするには、攻撃者はそのサブリソースを取得する際にサーバーがエラーを返すようにする必要があります。これを実現するには、いくつかの方法があります。 ブラウザによってリクエストが開始され、新しいコンテンツを受け取る前にAbortController.abort()で中止された cache:'reload'オプション付きのフェッチリクエスト 長大なRefererヘッダと 'cache':'reload'を持つリクエスト。ブラウザはこれを防ぐためにリファラの長さに上限を設けているので、これはうまくいかないかもしれません。 POSTリクエストに fetch no-cors を指定した場合。エラーが返されない場合でも、ブラウザがキャッシュを無効化することがあります。 Content-Type、Accept、Accept-Languageなど、サーバーを失敗させる可能性のあるリクエストヘッダ。(よりアプリケーションに依存します) その他のリクエストプロパティ これらの方法のいくつかは、しばしばブラウザのバグとみなされていそうです。(例: this bug). Origin ReflectionによるCORS error # Origin Reflectionは、グローバルにアクセス可能なリソースに、リクエストを初期化したオリジンを反映したAccess-Control-Allow-Origin(ACAO)ヘッダを付与する挙動のことです。これは、CORSの設定ミス3と考えることができ、ブラウザのキャッシュにリソースが存在するかどうかを検知するのに使用できます。 info 例えば、Flask frameworkにおいて、origin refrectionはデフォルトの動作になっています。 もし server.com にホストされているリソースが target.com からリクエストされた場合、オリジンはレスポンスヘッダに次のように反映されるでしょう。Access-Control-Allow-Origin: target.com といった具合です。リソースがキャッシュされている場合、この情報はリソースと一緒にブラウザのキャッシュに保存されます。これにより、もし attacker.com が同じリソースを取得しようとした場合、2つの可能性があります。 リソースがキャッシュにない場合:リソースは Access-Control-Allow-Origin: attacker.com ヘッダーとともにフェッチされ、保存される可能性があります。 リソースがすでにキャッシュにある:フェッチ試行はキャッシュからリソースをフェッチしようとするが、要求しているオリジンと ACAO ヘッダーの値の不一致によりCORSエラーが起こる。(target.comが期待されているが、実際に提供されたのはattacker.com 。)以下に、この脆弱性を悪用して被害者のブラウザのキャッシュ状態を推測するコード例を示します。 // この関数は、単にURLを受け取り、CORS モードでfetchします。 // fetchでエラーが発生した場合、attacker. ...