CSPは「最後の砦」ではない、それは「ブラウザの脳を縛る契約書」だ
多くのエンジニアがCSP(Content-Security-Policy)を「とりあえず設定しておくセキュリティヘッダー」程度に捉えている。しかし、現実のインシデント現場において、XSSが引き金となってペイロードが実行される瞬間、攻撃者はブラウザという巨大な実行エンジンを完全に掌握している。
脆弱性の根本原因がメモリ破壊であろうが、ロジックの不備であろうが、最終的に攻撃者が目指すのは「ブラウザというサンドボックス内での任意コード実行」だ。CSPは、その実行権限に対する「法的拘束力」を持つ数少ない防衛層である。これを単なるリストの列挙と捉えるか、プロトコルレベルの制約として設計するかで、君たちのアーキテクチャの価値は大きく変わる。
—
盲点:なぜ「緩和策」が「突破口」になるのか
多くのプロジェクトで目にする unsafe-inline や unsafe-eval の安易な利用。これらは、CSPという防衛ラインに自ら穴を開ける行為に他ならない。特に、生成AIが生成したコードや、サードパーティの依存ライブラリが複雑に絡み合う現代のフロントエンドにおいて、CSPは「信頼の境界線」を明確にする唯一のアーキテクチャだ。
攻撃者は、CSPのディレクティブをバイパスするために、JSONPエンドポイントの悪用や、サーバーサイドの挙動を模倣したDOMの書き換えを狙う。我々が設計すべきは、単なるホワイトリストではない。「実行されるべきコードの署名(Nonce)」と「実行される場所の厳格な分離」である。
—
鉄壁のCSP設計:実用的な戦略的ディレクティブ
以下に、現代の堅牢なWebアプリケーションが採用すべきCSPの構成例を示す。これは単なる設定値ではなく、ブラウザに対する「厳格な執行ポリシー」である。
推奨するCSP設定の骨子(Content-Security-Policy ヘッダー)
Content-Security-Policy:
default-src ‘none’;
# デフォルトは全て拒否。これが基本原則。
script-src ‘strict-dynamic’ ‘nonce-random123’ ‘unsafe-inline’ https:;
# ‘strict-dynamic’により、信頼されたスクリプトが動的に読み込むJSを許可。
# nonceにより、インラインスクリプトの実行を限定的に許可。
# レガシーブラウザ向けに’unsafe-inline’を残すが、nonceがあれば無視される。
connect-src ‘self’ https://api.trusted-service.com;
# XHR/Fetchの送信先を厳格に制限。エグスフィルトレーションを阻止する。
object-src ‘none’;
# プラグイン(Flashなど)の実行を完全に禁止。
base-uri ‘self’;
#
frame-ancestors ‘none’;
# クリックジャッキング対策。UIレッドレッシングを無効化。
1. strict-dynamic の活用
現代のSPA(Single Page Application)開発において、一つひとつのスクリプトのハッシュを管理するのは現実的ではない。strict-dynamic は、信頼されたメインスクリプト(nonce付きで読み込まれたもの)が、その配下で読み込むスクリプトを自動的に信頼する仕組みだ。これにより、運用の泥沼化を防ぎつつ、セキュリティレベルを維持できる。
2. nonceによる「実行の正当性」の証明
nonce(Number used once)は、リクエストごとに生成するべきだ。サーバーサイドで生成し、CSPヘッダーとHTMLの タグの両方に埋め込む。これにより、攻撃者が外部から注入したスクリプトにはnonceが付与できず、ブラウザによって即座に拒絶される。
---
インシデントハンドリングとガードレイルの視点
生成AIの活用が進む現在、プロンプトインジェクションによって生成された悪意あるコードが、そのままDOMに挿入されるケースが増えている。この時、CSPはフロントエンドにおける最後の「ガードレイル」となる。
- 監査の重要性: CSPの違反報告を
report-toやreport-uriで収集し、監視基盤(SIEM)に流し込むこと。これがなければ、攻撃者が何回扉を叩いたか、どこで失敗したかを知る術がない。 - 通信プロトコルの欠陥: CSPをバイパスする試みとして、HTTPレスポンス分割やキャッシュポイズニングが挙げられる。これらに対処するには、CSPだけでなく、HSTSやSecure属性を含めたトランスポート層の硬化が不可欠だ。
---
最後に:完璧を求めるな、回復力を求めよ
セキュリティとは「状態」ではなく「プロセス」だ。いくら強固なCSPを書いても、将来的に新しいブラウザの仕様や未知の脆弱性がその壁をすり抜ける可能性はある。
重要なのは、「もしCSPが突破されたら、その次に何が待ち受けているか」を設計することだ。サーバー側のバリデーション、DBのクエリパラメータの分離、そして万が一の侵害を検知するログの網羅性。これらを組み合わせた多層防御こそが、真のホワイトハッカーの設計思想である。
今日から、プロジェクトのヘッダーを見直してほしい。unsafe-inline の文字が、君たちのアプリケーションの寿命を縮めていないだろうか?
コメント