CSPを「ただの制約」と捉えるな:XSS防御の最前線で求められるアーキテクチャ思考
多くの開発者がCSP(Content-Security-Policy)を「ブラウザのセキュリティ設定」という程度の認識で扱っている。だが、現場の最前線に立つ我々にとって、CSPは「信頼の境界を宣言するコード」であり、悪意あるペイロードがブラウザのメモリ空間で実行される直前に突きつける「最後の検問所」だ。
特に`script-src`ディレクティブの設計を誤れば、それはセキュリティ層ではなく、単なる「形骸化した設定」に成り下がる。今回は、脆弱性ハンターたちが狙う「CSPの盲点」を突きつつ、堅牢な防御層を構築するための本質的な話をしよう。
—
1. なぜ `unsafe-inline` は「死の宣告」なのか
まず、大前提を叩き込む。`script-src ‘unsafe-inline’` を許可している時点で、XSS防御は崩壊している。攻撃者は、DOMベースの脆弱性を利用して、動的に生成されたスクリプトをメモリ上に展開し、実行する。
最新のモダンブラウザは強力だが、`unsafe-inline` があれば、攻撃者が注入した悪意あるスクリプトは、正規のスクリプトと区別されることなく実行コンテキストに取り込まれる。これを防ぐ唯一の解は、「スクリプトの出処と整合性を厳密に検証すること」に尽きる。
—
2. 現代の標準:NonceとHashを用いた動的防御
静的なホワイトリスト(ドメイン許可)は、CDNやサードパーティライブラリの脆弱性に引きずられるため、もはや推奨されない。現代のアーキテクトが選ぶべきは、Nonce(Number used once)による実行制御だ。
Nonceの実装イメージ
すべてのリクエストごとに暗号学的に安全な乱数を生成し、サーバー側でHTTPレスポンスヘッダとHTMLタグに埋め込む。
HTTPレスポンスヘッダの例
Content-Security-Policy: script-src ‘nonce-dGhlc2VjcmV0’;
ここで重要なのは、Nonceはセッションごと、あるいはリクエストごとに必ず再生成することだ。再利用すれば、それは固定値となり、攻撃者の格好の標的になる。
—
3. なぜ「ホワイトリスト」が回避されるのか
多くのエンジニアが犯すミスは、`script-src https://trusted.cdn.com` のようにドメイン単位で許可を与えてしまうことだ。
攻撃者は、そのドメイン上に存在する「古いライブラリ(例えば脆弱性のあるAngularJSの旧版や、JSONPエンドポイント)」を悪用する。これらはホワイトリストに含まれる「信頼されたドメイン」の一部であるため、CSPを華麗にすり抜ける。
これを防ぐには、`strict-dynamic` の活用を検討せよ。
堅牢な設定の例
Content-Security-Policy: script-src ‘nonce-random’ ‘strict-dynamic’ https:;
`strict-dynamic` を使用すると、Nonceで許可されたスクリプトが動的にロードしたサブスクリプトに対しても、自動的に実行権限が与えられる。これにより、複雑な依存関係を管理しつつ、インラインスクリプトの注入を完全に遮断できる。
—
4. 生成AI時代のプロンプトインジェクションとCSPの接点
今、我々が直面している最大の脅威の一つが、生成AIの出力内容をフロントエンドで直接レンダリングする際に発生する「プロンプトインジェクション」経由のXSSだ。
AIが生成した文字列に `

コメント