【テクニカル・上級編】Content-Security-Policy (CSP) のscript-srcディレクティブ詳細設定 – アプリケーションセキュリティ & 安全な開発防御ガイド

XSS防御の終着点:CSP script-src で「無力化」という名の聖域を築く

XSS(クロスサイトスクリプティング)は、もはや「古い脆弱性」などではない。攻撃の主戦場がSPA(Single Page Application)やサーバーサイドレンダリングの複雑な交差点へと移り変わる中で、我々が直面しているのは、単なるスクリプト注入を超えた「ブラウザの実行コンテキストの乗っ取り」である。

多くのエンジニアが「入力値のサニタイズ」という泥沼で溺れている間に、ハッカーはDOMの動的な生成過程や、ライブラリの依存関係に潜む脆弱性を突いてくる。今、我々に必要なのは、アプリケーションコードの不完全さを前提とした「ブラウザ側での強制的な防御レイヤー」、すなわち Content-Security-Policy (CSP) の厳格な設計だ。

なぜ unsafe-inline は「死の宣告」なのか

多くの現場で目にする script-src 'unsafe-inline'。これは、家中の窓を全開にして、泥棒に向かって「ここから入ってください」と看板を出しているようなものだ。

攻撃者は、反射型XSSでJavaScriptを注入する際、

ここで重要なのは、Nonceは静的なキャッシュを許さないということだ。CDN等でページをキャッシュする場合、レスポンスごとにこの値を書き換える必要がある。面倒? その「手間」こそが、攻撃者にとっての「壁」となるのだ。

ハッシュ(Hash)を用いた静的リソースの固定

外部スクリプトを読み込めない、あるいはページキャッシュを最大限に活用したいという制約があるなら、スクリプトの中身そのものをハッシュ値でホワイトリスト化する。

スクリプトの内容(SHA-256)を事前に計算して固定
Content-Security-Policy: script-src 'sha256-R43...(中略)...=' 'self';

この手法は、サードパーティ製ライブラリのCDN読み込みなど、内容が頻繁に変わらないリソースに対して絶大な効果を発揮する。もし攻撃者がスクリプト内のコードを1バイトでも改ざんすれば、ハッシュ値が一致しなくなり、ブラウザは即座に実行を拒否する。

生成AI時代の新たな脅威:ガードレイルとしてのCSP

昨今、プロンプトインジェクションによって生成AIが「悪意のあるHTMLやスクリプトを出力させられる」ケースが増加している。AIが生成したコードは、それが安全かどうかをAI自身が判断できないことが多い。

ここでCSPが最後のガードレイルとなる。アプリケーション側でAIの出力をレンダリングする際、HTMLに nonce を付与できない構造であれば、CSPの object-src 'none' や base-uri 'self' を併用し、攻撃の伝搬を物理的に断ち切る必要がある。

監査の視点:アーキテクトへの問い

私がセキュリティ監査を行う際、必ずチェックするのが以下の3点だ。

1. unsafe-eval は根絶されているか?: eval() や setTimeout(string) を使用するレガシーなJSが存在しないか。
2. base-uri の設定: タグによる相対パスの乗っ取りを防いでいるか。
3. report-uri または report-to: CSP違反が発生した際、そのログはどこに集約されているか。

特に3番目は重要だ。CSPは単なる防衛壁ではなく、「攻撃の予兆を検知するセンサー」でもある。CSP違反ログを解析すれば、どこでXSSが試行されているか、どの脆弱なライブラリが攻撃の踏み台にされているかが手に取るように分かるはずだ。

結論:完璧な防御は「制約」から生まれる

セキュリティとは、自由を奪うことではない。「何が許され、何が許されないのか」を明確な数学的根拠(暗号論的ハッシュや乱数)に基づいて定義し、それをマシンレベルで強制することだ。

unsafe-inline を排除し、Nonceとハッシュでスクリプトの実行を封鎖する。この泥臭くも精緻な設計を積み重ねることで初めて、我々は攻撃者に対して「このアプリケーションを攻略するのは、コストに見合わない」という諦めを突きつけることができる。

技術は日々進化するが、防御の基本原理は変わらない。攻撃者の視点を持ち、ブラウザという強力なエージェントをいかに味方につけるか。それこそが、我々エンジニアが追求すべき本質的なアーキテクチャである。

コメント

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