【実務・中級編】CSPにおけるstrict-dynamicの活用と後方互換性 – アプリケーションセキュリティ & 安全な開発防御ガイド

CSPの「strict-dynamic」でXSSを根絶する:ホワイトハッカーが教える現代的な防御戦略

現場でインシデント対応をしていると、「CSP(Content Security Policy)を設定しているのにXSSを突破された」という嘆きを耳にすることがあります。その原因の多くは、unsafe-inlineを安易に許可していたり、ホワイトリスト方式の運用が破綻して、結局ガバガバな設定になっているケースです。

今日は、現代のWebアプリケーションにおいて「XSSを実質無効化する」ための最強のカード、strict-dynamicについて解説します。教科書的な説明は抜きにして、なぜこれを使うべきなのか、どう実装すれば現場で動くのか、その核心に切り込みましょう。

—

1. なぜ従来のCSPは「負け戦」なのか

かつてのCSPは、信頼するドメインを羅列するホワイトリスト方式(script-src 'self' https://trusted.com など)が主流でした。しかし、これには致命的な欠点があります。

  • ホワイトリストの崩壊: trusted.com 上にたまたま存在していた古いJSONPエンドポイントや、AngularJS等のライブラリを攻撃者に悪用され、結局インジェクションを許してしまう。
  • 管理コストの爆発: CDNや外部スクリプトが増えるたびにヘッダーを修正する必要があり、運用が耐えられなくなる。

そこで登場したのが、「信頼されたスクリプトから動的に生成されたスクリプトは、たとえ外部からであっても実行を許可する」という考え方、strict-dynamicです。

—

2. strict-dynamic の仕組みとPoCの視点

攻撃者が狙うのは、HTML内に埋め込まれた や、属性ベースのイベントハンドラー(onmouseover等)です。

strict-dynamic を導入すると、ブラウザは「信頼されたソース(nonceやハッシュが付与されたスクリプト)が生成した子要素のみを信頼する」ようになります。これにより、攻撃者が外部から注入した野良スクリプトは、どんなに頑張っても実行されません。

攻撃者から見た「突破不能な壁」

攻撃者が https://attacker.com/malicious.js をロードしようとしても、それが信頼されたスクリプト(nonceを持つ親スクリプト)によって動的に生成されたものでなければ、ブラウザは即座にブロックします。これが「防弾」と呼ばれる理由です。

—

3. 実装のベストプラクティス(コピペ用サンプル)

strict-dynamic を使う際は、必ず nonce(毎回ランダムに生成される文字列)を併用します。

PHPでのヘッダー出力例

まずはバックエンドでnonceを生成し、ヘッダーとHTMLの両方に埋め込みます。


なぜこれで安全なのか(後方互換性の魔法)

  • モダンブラウザ: strict-dynamic を解釈し、'unsafe-inline' や https: を無視して、nonce が一致するスクリプトのみを信頼します。
  • 古いブラウザ: strict-dynamic を解釈できないため、フォールバックとして指定した 'unsafe-inline' や https: を参照します。これにはリスクが伴いますが、少なくとも「動かない」よりは「最低限のガードがある」状態を維持できます。

—

4. インフラ側で締め上げる(Nginx設定)

アプリケーション側だけでなく、WAFやリバースプロキシでもCSPを制御しておくのがプロの流儀です。Nginxであれば以下のように設定します。

Nginx設定ファイル
add_header Content-Security-Policy “script-src ‘nonce-RANDOM_NONCE_PLACEHOLDER’ ‘strict-dynamic’ ‘unsafe-inline’ https:; object-src ‘none’;”;

※実際には、nginxの sub_filter モジュール等を使って、動的に生成したnonceをレスポンス本文に注入する工夫が必要です。

—

5. 現場のエンジニアへ送るアドバイス

最後に、現場で運用する上での注意点を一つ。
strict-dynamic を導入すると、インラインイベントハンドラー(

コメント

タイトルとURLをコピーしました