CSP Level 3とstrict-dynamic:フロントエンドの「聖域」を守る最後の砦
多くの開発者がCSP(Content Security Policy)を「面倒な制約」と見なしている。特に大規模なマイクロフロントエンドや、サードパーティ製SDKが乱立する現代のWebアプリケーションにおいて、厳格なポリシーはしばしば「画面が真っ白になる」という結果を招くからだ。
しかし、攻撃者の視点から見れば、XSS(クロスサイトスクリプティング)は依然としてWebセキュリティの最前線であり、特にインジェクションの脆弱性は、メモリ上の制御フローを奪うための極めて効率的な足がかりとなる。本稿では、レガシーなホワイトリスト方式の限界を突破し、現代的なアプリケーションに適応するstrict-dynamicを用いた防衛戦略を、アーキテクトの視点から紐解く。
—
なぜ「ホワイトリスト」は死んだのか?
かつて、私たちはscript-src 'self' https://trusted.cdn.comのように、信頼できるドメインを並べることで防御を試みた。しかし、これは「JSONPエンドポイント」や「ライブラリのバージョン差異」といった盲点を突かれると瞬時に瓦解する。
攻撃者が狙うのは、CDN上にホストされた「古い、脆弱なライブラリ」だ。信頼されたドメインから、意図せずして悪意のあるスクリプトを読み込ませる手法は、もはや古典的だが極めて強力だ。ホワイトリストを保守することは、泥沼の戦いであり、本質的なセキュリティ向上には繋がらない。
strict-dynamic:信頼の「連鎖」を制御する
CSP Level 3で導入されたstrict-dynamicは、このパラダイムを根本から変えた。「ドメインを信頼する」のではなく、「信頼されたスクリプトが生成したコードのみを信頼する」という、コンテキストベースの防御モデルへの転換だ。
実装の骨子
strict-dynamicを使用する際は、ブラウザの互換性を考慮し、以下のようにポリシーを構成する。
CSPヘッダーの例
Content-Security-Policy:
script-src
‘nonce-R4nd0mStr1ng’ # 1. 信頼されたオリジンを示すノンリ
‘strict-dynamic’ # 2. ノンリで実行されたスクリプトが生成する子スクリプトも許可
‘unsafe-inline’ # 3. レガシーブラウザ向けフォールバック(ノンリがあれば無視される)
https: # 4. レガシーブラウザ向けフォールバック
http:; # 5. レガシーブラウザ向けフォールバック
なぜこれが強力なのか
1. Nonceによる認証: サーバー側で生成したランダムなnonceを持たないスクリプトは、インジェクションされても実行されない。
2. 連鎖の許可: 最初の(信頼された)スクリプトが動的に document.createElement('script') を行い、子スクリプトをロードする場合、そのロードも自動的に許可される。
3. 無効化の強制: Modernなブラウザでは、strict-dynamicが存在すると、ホワイトリスト(https://trusted.cdn.com等)は無視される。つまり、管理外のスクリプトを読み込む隙間を物理的に塞ぐことができる。
—
アーキテクチャ設計における「ガードレイル」の構築
単にヘッダーを付与するだけでは、真の防御は完成しない。以下のポイントを設計指針に加えてほしい。
1. サーバーサイド・ノンリ生成の完全性
nonceは、リクエストごとに生成し、かつ予測不可能(暗号論的に安全な乱数)でなければならない。キャッシュの罠に注意せよ。もしnonceが静的に固定されていたり、CDNでキャッシュされたりすれば、それはもはや鍵ではなく、公開されたパスワードと同義だ。
2. 生成AIによるプロンプトインジェクションへの応用
現在、LLMをフロントエンドに統合するアプリケーションが増えている。LLMが生成したコンテンツをそのままDOMに挿入するような実装は、まさに「自爆」に近い。
ここで、strict-dynamicと組み合わせた「サンドボックス化されたiframe」の利用を推奨する。LLMが生成したコードは、信頼された親スクリプトのnonceを受け継ぐことなく、独立したコンテキストで実行させることで、万が一のインジェクションをブラウザのサンドボックス層で遮断できる。
3. レポーティングによる監査の自動化
report-to または report-uri は、単なるログ出力ではない。これは「攻撃のプロファイリング」ツールだ。
CSP違反が発生した際、どのドメインが、どのスクリプトパス経由で悪意のある通信を試みたかを解析することで、社内ネットワークから漏洩した認証情報の不正利用や、サードパーティSDKのサプライチェーン攻撃をリアルタイムで検知できる。
—
現場のエンジニアへ:明日からのアクション
1. 監査: 現在のサイトの script-src を確認せよ。ホワイトリストが10個以上並んでいるなら、それは既に「崩壊している」のと同じだ。
2. 移行: まずは report-only モードで nonce を導入し、どのスクリプトがブロックされるかを確認する。
3. カプセル化: 外部SDKを直接DOMに埋め込まず、Nonceを持つエントリポイントスクリプトから動的にロードするようにラッパーを設計する。
我々が戦っている相手は、OSのメモリレイアウトを熟知し、ブラウザのパース処理の隙間を縫うような攻撃者だ。教本通りの設定に安住するのではなく、ブラウザの実行モデルを深く理解し、strict-dynamicのような堅牢な制御機構を「コードの呼吸」として実装すること。それが、現代のセキュリティアーキテクトに求められる泥臭くも高潔な仕事だ。
セキュリティは、設定ファイルの中にではなく、その設計思想の中に宿る。健闘を祈る。
コメント