October 1, 2020
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 🔗 – 異なるシナリオに対して有効な防御機構を組み合わせる必要があります。
...
October 1, 2020
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をより大幅に緩和することができます。
...
October 1, 2020
リクエストのContent-Typeをリークすることで、攻撃者は2つのリクエストを区別できるようになります。
typeMustMatch # typeMustMatchはobject要素のtypeMustMatch属性を反映したブール値です。これは、オブジェクトを読み込む際に、リソースのContent-Typeがオブジェクトで提供されるものと同じかどうかを検証することで、特定の MIME タイプを強制しなければならないことを保証します。残念ながら、この強制は攻撃者がウェブサイトから返されるContent-Typeとステータスコードを漏らすことも可能にします 1。
根本的な原因 # 以下のスニペットを考えると、https://target/api で返された Content-Type が type のものと一致しない場合や、サーバが 200 以外のステータスを返した場合には、 not_loaded がレンダリングされるでしょう。
<object type="application/json" data="https://example.org" typemustmatch> not_loaded </object> 問題 # 攻撃者は、すべての条件が満たされたときに起こるオブジェクトのレンダリングを検出することによって、ウェブサイトのContent-Typeとステータスコードをリークできます。攻撃者は、(ステータスコード200で)オブジェクトがレンダリングされるとき、0ではないであろうclientHeightとclientWidthの値をチェックできます。typeMustMatchはリソースを読み込む際に、サーバーがステータス200を返すことを要求するので、Error Events XS-Leaksと同様にエラーページを検出することが可能でしょう。
以下の例では、iframe内にオブジェクトを埋め込み、iframeがonloadイベントをトリガーしたときにclientHeightとclientWidthの値をチェックすることでこの動作を検出する方法を示しています。
// 配送先のWebサイトのURLを設定する var url = 'https://example.org'; // チェックしたいコンテンツタイプ var mime = 'application/json'; var ifr = document.createElement('iframe'); // オブジェクトがonloadイベントを発生させないので、iframe内にオブジェクトをロードする。 ifr.srcdoc = ` <object id="obj" type="${mime}" data="${url}" typemustmatch> error </object>`; document.body.appendChild(ifr); // iframeが読み込まれたら、オブジェクトの高さを読み取ります。 // もしそれが一行のテキストの高さであれば、リソースのContent Typeは`application/json`ではなかったということです。 // もしそうでなければ、それは`application/json`だったということです。 ifr.onload = () => { console.
...