CSP Level 3の真髄:strict-dynamicで動的スクリプトの「信頼の連鎖」を構築する
セキュリティの世界では、長らく「ホワイトリストによる外部ドメイン指定」がCSP(Content Security Policy)の常識とされてきた。しかし、現代の複雑なフロントエンド・アーキテクチャにおいて、この手法はもはや「破綻した神話」に過ぎない。CDNを介して動的にスクリプトを読み込み、さらにそのスクリプトが別のモジュールを読み込むような環境で、厳密なドメイン指定を維持するのは不可能だ。
結果として、開発者は利便性を求めてunsafe-inlineを許可し、セキュリティの防壁を自ら無効化している。今回は、この泥沼から脱却し、現代的なWebアプリを「動的スクリプト」の脅威から守るための、strict-dynamicを用いた設計手法を紐解く。
—
なぜ従来のCSPは「穴だらけ」なのか
従来のCSP(Level 1/2)で外部ドメインをリストアップする手法には、致命的な欠陥がある。
1. JSONPエンドポイントの悪用: 許可されたドメイン内に存在するJSONPエンドポイントを悪用すれば、ホワイトリストを簡単にバイパスできる。
2. 管理コストの増大: 動的読み込みのたびにCSPヘッダーを書き換えるのは現実的ではない。
3. 信頼の帰属先: 「このドメインなら安全」という考え方自体が、サブドメインの乗っ取り(Subdomain Takeover)に対する脆弱性を内包している。
我々が目指すべきは、「ドメイン」で制御するのではなく、「信頼されたスクリプトが生成したスクリプト」を再帰的に信頼する「信頼の連鎖(Chain of Trust)」の確立だ。
—
strict-dynamic:信頼の伝播を設計する
strict-dynamicは、CSP Level 3で導入された救世主だ。これを有効にすると、ブラウザは以下の論理でスクリプトの実行を判断する。
- ノンス(Nonce)またはハッシュで認証されたルートスクリプトは、自身が生成した動的スクリプト(
document.createElement('script')等)の実行を自動的に許可する。 - これに加えて、
strict-dynamicを指定することで、信頼されたスクリプトが読み込んだ外部スクリプトも同様に信頼される。
これにより、開発者は「どのドメインを許可するか」という終わりのない戦いから解放され、「どのスクリプトがエントリーポイントか」を管理するだけで済むようになる。
実装例:堅牢なセキュリティヘッダー
以下は、モダンなアプリケーションで推奨されるCSPヘッダーの設定だ。
サーバーサイドで動的に生成したnonce値を付与する
Content-Security-Policy:
script-src
‘nonce-RzY0N2RhYz…’ # 信頼の起点となるスクリプトのNonce
‘strict-dynamic’ # このスクリプトが読み込む依存関係も自動的に信頼
‘unsafe-inline’ # レガシーブラウザ向けフォールバック(strict-dynamicがあれば無視される)
‘http:’ ‘https:’; # レガシーブラウザ向けフォールバック
object-src ‘none’; # プラグインの無効化(基本中の基本)
base-uri ‘self’; # ベースタグインジェクション対策
—
アーキテクトが注意すべき「落とし穴」
strict-dynamicは強力だが、魔法ではない。以下の点に留意しないと、セキュリティ強度は即座に低下する。
1. Nonceの予測可能性(Entropy)
Nonceは「推測不可能」でなければならない。サーバー側でリクエストごとに生成し、CSPRNG(暗号論的擬似乱数生成器)を使用することが絶対条件だ。キャッシュの影響を受けないよう、必ず動的生成を行うこと。
2. コンテキストの分離(ガバナンス)
strict-dynamicを許可すると、信頼されたエントリーポイントが侵害された場合、その範囲内で任意のスクリプトが実行可能になる。これは、「エントリーポイントの選定」がアプリケーションの境界防衛の最前線であることを意味する。
3. 生成AIとの共存:プロンプトインジェクションの脅威
現在、LLMがコードを生成してフロントエンドで実行するアプリが増えている。この際、LLMが生成したコードにnonceを埋め込むプロセスを正しく実装しなければ、AIが生成した「悪意ある動的スクリプト」をCSPで防ぐことはできない。AIの出力をそのままinnerHTMLに流し込むような設計は、今すぐ廃止すべきだ。
—
結論:防衛のレイヤーを一段深める
サイバー犯罪者は常に「設定の甘い隙間」を探している。strict-dynamicの採用は、単なる仕様への追従ではなく、「信頼の根拠をスクリプトの出自(Origin)から、スクリプトの認証(Authentication)へとシフトさせる」というアーキテクチャの転換である。
もし君が大規模なSaaSを運用しているのであれば、まず自社の全スクリプトをNonce化し、strict-dynamicを導入する計画を立ててほしい。それが、複雑化するフロントエンド環境における、エンジニアとしての最低限の誠実さであり、かつ最高峰の防御策となるはずだ。
インシデントは常に「想定外のスクリプト」から始まる。その入り口を、設計段階で物理的に閉ざす。それこそが、我々セキュリティアーキテクトの仕事だ。
コメント