はじめに
クロスサイトスクリプティング(XSS)の有効な対策として導入される Content Security Policy (CSP) ですが、実際に運用しようとすると、外部スクリプトのロード設定が非常に複雑になるという課題に直面します。
特にGoogleタグマネージャーやアナリティクス、各種広告タグ、SNS共有ボタンなどを導入しているWebサイトでは、各サービスが非同期的に他のドメインからJavaScriptを動的に読み込む(インジェクションする)ため、CSPの script-src に無数の外部ドメインをホワイトリストとして登録しなければならず、メンテナンスが困難になります。
この問題をスマートに解決するために導入されたのが、CSP レベル3の 'strict-dynamic' ディレクティブです。本記事では、この設定のメリットと動作原理を解説します。
1. 従来のドメインホワイトリスト型CSPの問題点
従来のCSP(レベル1〜2)では、読み込みを許可する外部ドメインを個別に指定する「ドメインホワイトリスト方式」が主流でした。
Content-Security-Policy: script-src 'self' https://www.google-analytics.com https://cdn.jsdelivr.net;
この方式が抱える課題
- メンテナンスの破綻:
外部のSDK(例:
google-analytics.com)がアップデートされ、内部でさらに別のドメイン(例:static.cloudflareinsights.com)から追加のヘルパースクリプトを読み込むよう変更された場合、突然Webサイト上でスクリプトがブロックされ、動作しなくなります。そのたびにCSPヘッダーを修正する必要があります。 - バイパス(防御突破)のリスク:
ホワイトリストに指定したCDN(例:
cdnjs.cloudflare.comなど)の中に、古い脆弱性のあるJavaScriptライブラリや、任意のコードを実行させられるコードがホストされている場合、XSS攻撃に悪用され、CSPをバイパスされてしまいます。
2. ‘strict-dynamic’ の仕組み
'strict-dynamic' は、ホワイトリスト方式の課題を解決するアプローチをとります。
基本ルール
- 信頼の連鎖 (Trust Chain):
'strict-dynamic'が指定されている場合、ブラウザは**「すでに認証(nonce や hash によって信頼)されたスクリプトが、動的に追加生成した新しいスクリプトタグ」を、自動的に信頼して実行を許可**します。 - ドメインホワイトリストの無効化:
'strict-dynamic'が有効になると、ブラウザはscript-src内に記述されているドメインリスト(https://...)や'self'を自動的に無視します。代わりに、nonceまたはhashの判定のみを信頼基準とします。
3. 具体的な設定例
'strict-dynamic' を適用する場合、通常は nonce(リクエストごとに生成されるランダムな値)または sha256 ハッシュ値と組み合わせて使用します。
HTTPレスポンスヘッダーの設定例
Content-Security-Policy: script-src 'nonce-RndOmValue123' 'strict-dynamic' 'unsafe-inline' https:;
'nonce-RndOmValue123': この一時値を持つスクリプトのみ実行を許可します。'strict-dynamic': 認証されたスクリプトが動的に作成した他のスクリプトの実行を無条件で許可します。'unsafe-inline' https:: これは、'strict-dynamic'に対応していない古いブラウザ(CSPレベル1〜2のみ対応)のための後方互換用(フォールバック)設定です。モダンブラウザではこれらは無視されます。
HTMLでのマークアップ例
<!-- nonce属性を付与することで、この親スクリプトは実行が許可される -->
<script nonce="RndOmValue123">
// この中で動的に作成されたスクリプトタグも、strict-dynamicの恩恵で自動的に許可される
const script = document.createElement('script');
script.src = 'https://example.com/dynamic-library.js';
document.head.appendChild(script);
</script>
この実装により、dynamic-library.js のドメイン(example.com)をCSPヘッダーにいちいち登録していなくても、親スクリプトが信頼されているため、ブラウザはブロックせずにロード・実行してくれます。
4. ‘strict-dynamic’ 導入時の注意点
パーサー挿入型のスクリプト
JavaScript内で document.write('<script src="..."></script>') のような形でスクリプトを挿入する古いライブラリ(Parser-inserted scripts)の場合、ブラウザによっては 'strict-dynamic' の信頼チェーンが切れてブロックされることがあります。
そのため、動的に追加するスクリプトは、常に document.createElement('script') を用いてDOMに挿入するモダンな記述に書き換える必要があります。
まとめ
CSPにおける 'strict-dynamic' の採用は、セキュリティ強度を高めつつ、外部SDKの読み込み設定の手間を劇的に削減するための最適解です。
- 外部ドメインの膨大なホワイトリスト管理から解放される
- 信頼されたスクリプトからの動的なスクリプト挿入(インジェクション)がスムーズになる
- 古いブラウザ向けに
'unsafe-inline' https:を併記して互換性を保つ
XSS対策を本気で実装する際は、この 'strict-dynamic' と nonce による安全なセキュリティ設計をぜひ検討してみてください。
