Web セキュリティ

CSP (コンテンツセキュリティポリシー)

約 4 分で読めます

CSP (Content Security Policy) とは

CSP (Content Security Policy) とは、Web ページが読み込めるリソース (スクリプト、スタイルシート、画像、フォントなど) の提供元をサーバー側で制御する HTTP レスポンスヘッダーです。ブラウザは CSP で許可されていないリソースの読み込みや実行をブロックするため、XSS (クロスサイトスクリプティング) で挿入されたスクリプトの実行や、盗み出した情報の外部への送信を妨げる層として働きます。

XSS の根本対策は出力エスケープですが、実装の漏れは完全には排除できません。CSP は「たとえ XSS 脆弱性が存在しても、攻撃者のスクリプトを実行させない」という多層防御の役割を果たします。ただし止められるのは「許可していない読み込み・実行」だけで、許可した範囲の中で成立する攻撃は防げません。セキュリティヘッダーの中では指定できる項目が多く、どこまで絞り込めたかがそのまま実効性になるヘッダーです。仕様は Level 2 が 2016 年 12 月 15 日に W3C 勧告となり、Level 3 は 2026 年 8 月時点でも W3C Working Draft として策定が続いています。

主要なディレクティブ

CSP は複数のディレクティブで構成され、リソースの種類ごとに許可する提供元を指定します。

  • default-src: 他のディレクティブで明示的に指定されていないリソースのデフォルトポリシー。default-src 'self' は同一オリジンからのリソースのみを許可する。フォールバックが効くのは読み込み系のディレクティブだけで、base-uri・form-action・frame-ancestors は default-src 'none' を書いても無指定のまま残るため個別に指定する
  • script-src: JavaScript の読み込み元を制御する。XSS 対策の核心となるディレクティブ。'unsafe-inline' を避け、nonce または hash を使用することが推奨される。nonce か hash を指定した時点で 'unsafe-inline' は無視される仕様のため、両方を並べる書き方が意味を持つのは nonce を解釈しない古い実装向けの互換維持に限られる
  • style-src: CSS の読み込み元を制御する
  • img-src: 画像の読み込み元を制御する
  • connect-src: fetch、XMLHttpRequest、WebSocket の接続先を制御する
  • font-src: Web フォントの読み込み元を制御する
  • frame-src: 自ページの iframe に読み込めるページの提供元を制御する (埋め込む側の制御)
  • frame-ancestors: 逆向きに、自サイトを iframe へ埋め込める親ページのオリジンを制御する (埋め込まれる側の制御)。X-Frame-Options の後継にあたり、クリックジャッキングの対策として指定するのはこちら

ソース値の指定方法

  • 'self': 同一オリジン
  • 'none': すべてブロック
  • 'nonce-{random}': 特定の nonce 値を持つインラインスクリプト/スタイルのみ許可
  • 'strict-dynamic': nonce または hash で許可されたスクリプトが動的に読み込むスクリプトも許可する。指定すると script-src に並べたドメインやスキーム、'self'、'unsafe-inline' はスクリプトの読み込み判定で無視され、nonce と hash だけが効く
  • 具体的なドメイン: https://cdn.example.com

段階的な導入手順

CSP の導入は、既存のサイトを壊さないよう段階的に進めることが重要です。

ステップ 1: Report-Only モードで監視

Content-Security-Policy-Report-Only ヘッダーを使用すると、ポリシー違反を検知してレポートするだけで、リソースのブロックは行いません。まずこのモードで現在のサイトがどのようなリソースを読み込んでいるかを把握します。

ステップ 2: 基本ポリシーの策定

レポートの分析結果に基づき、画像やスタイルなどの読み込み系ディレクティブから提供元を絞り込んでいきます。一方でスクリプトは、許可したドメイン上に残る JSONP エンドポイントやオープンリダイレクトが抜け道になり得ることが 2016 年の研究 (Lukas Weichselbaum ほか「CSP Is Dead, Long Live CSP!」) で整理されており、提供元の列挙 (ホワイトリスト) だけに頼らない設計が必要です。CSP Level 3 の仕様書も参考情報として、script-src に nonce または hash と 'strict-dynamic' を組み合わせ、base-uri を絞る構成を Strict CSP と呼び、実装しやすい緩和策として挙げています。

ステップ 3: 本番適用と継続的な監視

Content-Security-Policy ヘッダーに切り替えて本番適用します。report-to ディレクティブ (Level 3 では report-uri が非推奨となり、こちらへの置き換えが示されています) でポリシー違反のレポートを継続的に収集し、誤検知や新たなリソース要件に対応します。移行期は両方を書いておくと、片方しか解釈しない環境でもレポートを取りこぼしません。

推奨される厳格なポリシーの例

default-src 'none'; script-src 'self' 'nonce-{random}' 'strict-dynamic'; style-src 'self' 'nonce-{random}'; img-src 'self' data:; font-src 'self'; connect-src 'self'; frame-ancestors 'none'; base-uri 'self'; form-action 'self'

このポリシーは、インラインスクリプトを nonce で制御し、'strict-dynamic' で動的に読み込まれるスクリプトも許可します。'strict-dynamic' を解釈しないブラウザへの後方互換が必要な場合は、script-src に https: を併記する書き方が仕様の注記で示されています (解釈するブラウザ側では無視されます)。

CSP と他のセキュリティヘッダーの連携

CSP が判断できるのは、リソースを読み込んで実行してよいかどうかだけです。他のセキュリティヘッダーと組み合わせ、それぞれが受け持つ範囲を埋めていきます。

  • HSTS との併用: HTTPS を強制する。平文の HTTP で配信されたページは経路上で CSP ヘッダーごと削除・書き換えができるため、ポリシーが確実に届く配信経路を HTTPS で確保する
  • X-Content-Type-Options: nosniff: MIME タイプスニッフィングを防止し、スクリプトとして解釈されるべきでないリソースの実行を阻止する
  • CORS との関係: 働く向きが逆になる。CSP は自ページが読み込んでよい提供元を制限する側で、CORS は同一オリジンポリシーが既定で閉じている応答の読み取りを、サーバーの宣言で必要な範囲だけ開ける側にある

導入で手間がかかるのは、既存のインラインスクリプトやサードパーティのタグを洗い出し、nonce の付与や提供元の整理に置き換える作業です。この棚卸しは稼働年数が長いサイトほど量が増えるため、新規プロジェクトでは最初から nonce を前提にしたテンプレート構成にしておくと、XSS やクリックジャッキングへの対策を後追いで足す作業が要らなくなります。

よくある誤解

CSP を設定すれば XSS 対策は不要
CSP は XSS の被害を軽減する多層防御の一層であり、XSS の根本対策 (出力エスケープ) を代替するものではない。提供元の列挙 (ホワイトリスト) に頼ったポリシーの回避手法は 2016 年の研究 (Lukas Weichselbaum ほか「CSP Is Dead, Long Live CSP!」) で整理されており、CSP Level 3 の仕様書も nonce / hash と 'strict-dynamic' を用いた構成を推奨形として挙げている。出力エスケープと CSP の両方を実装することが前提となる。
CSP は設定が難しいので小規模サイトには不要
小規模サイトでも XSS のリスクは存在する。最小限のポリシー (例: default-src 'self'; script-src 'self') から始めれば導入は容易。Report-Only モードで影響を確認しながら段階的に厳格化できる。
共有する

関連用語

関連記事