CSP の XSS 攻撃に対する効果を確認する

コンテンツ セキュリティ ポリシー(CSP)を使用すると、ページに読み込まれるコンテンツがサイト所有者によって信頼されていることを確認できます。CSP は、攻撃者が挿入した安全でないスクリプトをブロックできるため、クロスサイト スクリプティング(XSS)攻撃を軽減します。ただし、CSP が十分に厳格でない場合、簡単に回避される可能性があります。詳しくは、厳格なコンテンツ セキュリティ ポリシー(CSP)でクロスサイト スクリプティング(XSS)を軽減するをご覧ください。Lighthouse は、メインのドキュメントに適用されている CSP を収集し、回避可能な問題については CSP エバリュエータから報告します。

適用モードで CSP が検出されないという Lighthouse レポートの警告。
適用モードで CSP が検出されないという警告が Lighthouse レポートに表示されます。

バイパス不可の CSP に必要な手法

CSP をバイパスできないようにするには、次の方法を実装します。CSP をバイパスできる場合、Lighthouse は重大度の高い警告を出力します。

CSP が XSS をターゲットとする

XSS をターゲットにするには、CSP に script-srcobject-srcbase-uri のディレクティブを含める必要があります。CSP にも構文エラーがないようにする必要があります。

script-srcobject-src は、それぞれ安全でないスクリプトと安全でないプラグインからページを保護します。また、script-srcobject-src などの多くのディレクティブの代わりに default-src を使用して、幅広いポリシーを構成することもできます。

base-uri は、すべての相対 URL(スクリプトなど)を攻撃者の管理するドメインにリダイレクトするために使用される、不正な <base> タグの挿入を防ぎます。

CSP は nonce またはハッシュを使用して、許可リストのバイパスを回避します

script-src の許可リストを構成する CSP は、信頼できるドメインから送信されるすべてのレスポンスが安全であり、スクリプトとして実行できるという前提に基づいています。ただし、この前提は最新のアプリケーションには当てはまりません。JSONP インターフェースを公開したり、AngularJS ライブラリのコピーをホストしたりするなど、一般的な無害なパターンでは、攻撃者が CSP の制限を回避できます。

実際には、アプリの作成者には明らかでないかもしれませんが、script-src 許可リストのほとんどは XSS バグのある攻撃者によって回避可能であり、スクリプト インジェクションに対する保護はほとんどありません。一方、ノンスベースとハッシュベースのアプローチでは、このような問題は発生せず、より安全なポリシーを採用して維持しやすくなります。

たとえば、次のコードでは、信頼できるドメインでホストされている JSONP エンドポイントを使用して、攻撃者が制御するスクリプトを挿入します。

CSP:

script-src https://trusted.example.com

HTML:

<script src="https://trusted.example.com/path/jsonp?callback=alert(document.domain)//"></script>

回避を防ぐには、CSP で nonce または hash を使用してスクリプトを個別に許可し、許可リストではなく「strict-dynamic」を使用する必要があります。

安全な CSP に関するその他の推奨事項

セキュリティと互換性を強化するには、次の方法を実装します。CSP が推奨事項のいずれかに準拠していない場合、Lighthouse は中程度の重大度の警告を出力します。

CSP レポートを設定する

報告先を構成すると、破損をモニタリングできます。報告先は、report-uri ディレクティブまたは report-to ディレクティブを使用して設定できます。report-to は現在、すべてのモダン ブラウザでサポートされているわけではないため、両方を使用するか、report-uri のみを使用することをおすすめします。

コンテンツが CSP に違反している場合、ブラウザは設定された宛先にレポートを送信します。この転送先に、これらのレポートを処理するアプリケーションが構成されていることを確認してください。

HTTP ヘッダーで CSP を定義する

CSP は次のようにメタタグで定義できます。

<meta http-equiv="Content-Security-Policy" content="script-src 'none'">

ただし、可能であれば HTTP レスポンス ヘッダーで CSP を定義する必要があります。メタタグの前に挿入すると、CSP を回避できます。また、メタタグ CSP では frame-ancestorssandbox、レポートはサポートされていません。

CSP が下位互換性があることを確認する

すべてのブラウザが CSP nonce/hash をサポートしているわけではないため、準拠していないブラウザのフォールバックとして unsafe-inline を追加することをおすすめします。ブラウザが nonce/hash をサポートしている場合、unsafe-inline は無視されます。

同様に、strict-dynamic はすべてのブラウザでサポートされているわけではありません。ポリシーに準拠していないブラウザに対しては、許可リストを代替として設定することをおすすめします。strict-dynamic をサポートしているブラウザでは、許可リストは無視されます。

厳格な CSP を開発する方法

以下は、ノンスベースのポリシーで厳格な CSP を使用する例です。

CSP:

script-src 'nonce-random123' 'strict-dynamic' 'unsafe-inline' https:;
object-src 'none';
base-uri 'none';
report-uri https://reporting.example.com;

HTML:

<script nonce="random123" src="https://trusted.example.com/trusted_script.js"></script>

random123 は、ページが読み込まれるたびにサーバーサイドで生成される base64 文字列です。unsafe-inlinehttps: は、ノンスと strict-dynamic が原因で最新のブラウザでは無視されます。厳格な CSP の導入について詳しくは、厳格な CSP ガイドをご覧ください。

Lighthouse と CSP 評価ツールを使用して、CSP に潜在的なバイパスがないかどうかを確認できます。既存のページを破損するリスクなしで新しい CSP をテストするには、ヘッダー名として Content-Security-Policy-Report-Only を使用して、レポート専用モードで CSP を定義します。これにより、report-toreport-uri で構成したレポート先に CSP 違反が送信されますが、実際に CSP が適用されることはありません。