Content Security Policy(CSP)入門 - XSS を緩和するブラウザの防御層を設計する

Content Security Policy(CSP)入門 - XSS を緩和するブラウザの防御層を設計する

作成日:
読了:16
更新日:
この記事を読む人におすすめPR / Amazonアソシエイト

当サイトは Amazon.co.jp を宣伝しリンクすることで紹介料を得る手段を提供する、Amazonアソシエイト・プログラムの参加者です。価格・在庫はリンク先の最新情報をご確認ください。

XSS(クロスサイトスクリプティング)対策というと、まず出力エスケープやサニタイズを思い浮かべます。ですが、テンプレートのどこか一箇所でエスケープを漏らせば攻撃は成立します。そこで「万一スクリプトが注入されても、ブラウザ側でその実行を止める」二重の防御層を用意するのが Content Security Policy(CSP) です。

この記事では、CSP の仕組みと実務での設計方針を、MDN・W3C CSP Level 3・web.dev を一次ソースに整理します。同じくブラウザのセキュリティ機構である CORS の仕組み と併せて押さえると、Web セキュリティの土台が固まります。

CSP とは何か、何を緩和するか

CSP は「このページでは、どこから読み込んだリソースを、どの種類だけ許可するか」をブラウザに宣言する仕組みです。サーバが HTTP レスポンスヘッダでポリシーを送り、ブラウザがそれに反するリソースの読み込みやスクリプト実行をブロックします。

主眼は XSS の緩和です。攻撃者がページに <script> を注入できたとしても、CSP が「許可されていないスクリプトは実行しない」と決めていれば、注入されたコードは動きません。ほかにも次を緩和できます。

  • インラインスクリプトやインラインイベントハンドラの実行を止める
  • frame-ancestors で他サイトへの埋め込み(クリックジャッキング)を制限する
  • upgrade-insecure-requests で混在コンテンツ(HTTP 読み込み)を抑える

IMPORTANT

CSP は XSS を「根絶」する仕組みではなく、被害を緩和する多層防御の一層です。第一の対策はあくまで入力検証と出力エスケープです。実際に React などでも CVE-2025-55182 のようなインジェクション由来の脆弱性 が起こり得ます。CSP はそうした「漏れ」が起きたときの最後の緩衝材として設計します。

基本の書き方(ヘッダとディレクティブ)

CSP は Content-Security-Policy レスポンスヘッダで送るのが基本です。値はディレクティブをセミコロンで区切って並べます。

もっとも基本的な CSP
Content-Security-Policy: default-src 'self'; img-src 'self' data:; object-src 'none'

これは「原則すべて自オリジンのみ、画像は自オリジンと data URI を許可、<object> は全面禁止」という意味です。各ディレクティブは「名前 + ソースリスト」で構成されます。

指定手段は3つあります。

  • Content-Security-Policy ヘッダ: 標準。すべてのディレクティブが使え、これが推奨
  • Content-Security-Policy-Report-Only ヘッダ: ポリシーを強制せず違反レポートだけ送る。段階導入に使う
  • <meta http-equiv> タグ: HTML 内で指定。ただし対応しないディレクティブがあり(frame-ancestorsreport-uri など)、Report-Only も使えない。SPA など限定的な用途向け

NOTE

ヘッダはメインの HTML だけでなく、原則すべてのレスポンスに付けるのが MDN の推奨です。CSP は文書ごとに適用されるため、各 HTML 応答に確実に付与する運用にします。

主要ディレクティブ早見

ディレクティブは大きく「リソース取得を制御するもの」と「文書全体の挙動を制御するもの」に分かれます。取得系は原則、指定がなければ default-src の値にフォールバックします。

ディレクティブ制御対象
default-src未指定の取得系ディレクティブのフォールバック
script-srcJavaScript の読み込みと実行
style-srcCSS スタイルシート
img-src画像
connect-srcfetch / XMLHttpRequest / WebSocket / EventSource の接続先
font-srcWeb フォント
frame-src埋め込む <iframe> の読み込み元
object-src<object> / <embed>(プラグイン)
media-src音声・動画
manifest-srcWeb アプリマニフェスト

文書全体の挙動を制御する代表格は次のとおりです。

ディレクティブ役割
frame-ancestorsこのページを埋め込める側を制限(クリックジャッキング対策)
base-uri<base> 要素で設定できる URL を制限
form-actionフォームの送信先を制限
upgrade-insecure-requestsHTTP のサブリソースを自動的に HTTPS へ格上げ
report-to / report-uri違反レポートの送信先

NOTE

frame-ancestors は、古い X-Frame-Options ヘッダの上位互換にあたる制御です。複数のオリジンを許可でき、より柔軟にクリックジャッキング対策ができます。

ソースキーワードと 'unsafe-inline' の危険

ソースリストには、ホスト名(example.com*.example.org)やスキーム(https:data:)のほか、次のキーワードが使えます。

  • 'self': 文書と同一オリジン
  • 'none': 一切許可しない
  • 'unsafe-inline': インラインの <script> / スタイルやイベントハンドラを許可
  • 'unsafe-eval': eval()Function()、文字列を渡す setTimeout などを許可

script-src(または default-src)が指定されると、既定ではインラインスクリプトが全面的にブロックされます。具体的には次が動かなくなります。

  • インラインの <script> ブロック
  • onclick= などのインラインイベントハンドラ属性
  • javascript: URL

これを一括で復活させるのが 'unsafe-inline' ですが、これを付けた瞬間 CSP の XSS 防御はほぼ無力化します。攻撃者が注入する XSS ペイロードもインラインスクリプトだからです。同様に 'unsafe-eval'eval 系を通してしまうため、原則避けます。

WARNING

「CSP を入れたら既存のインラインスクリプトが全部止まった」からといって、安易に 'unsafe-inline' で逃げてはいけません。それは鍵をかけた玄関を開けっぱなしにするのと同じです。次に述べる nonce か hash で、必要なスクリプトだけを個別に許可します。

nonce と hash による strict CSP

ホスト名の許可リスト(allowlist)方式の CSP、たとえば script-src https://apis.example.com は運用が難しく、かつバイパスされやすいことが Google の研究で示されています。許可したドメイン上に JSONP エンドポイントや古いライブラリがあると、そこを踏み台にポリシーを回避されるためです。

そこで web.dev が推奨するのが、nonce または hash を使った strict CSP です。URL ではなく「このスクリプトは自分が意図したものだ」という暗号的な印で許可します。

nonce 方式(動的なページ向け)

nonce は「1回だけ使うランダム値」です。サーバがレスポンスごとに新しいランダム値を生成し、CSP ヘッダと <script> タグの両方に同じ値を書きます。攻撃者はその時々の nonce を知り得ないため、注入したスクリプトを実行できません。

nonce ベースの strict CSP
Content-Security-Policy: script-src 'nonce-r4nd0m123' 'strict-dynamic'; object-src 'none'; base-uri 'none'
対応する HTML(同じ nonce を付与)
<script nonce="r4nd0m123" src="/app.js"></script>
<script nonce="r4nd0m123">console.log('hello');</script>

nonce の条件は、128 ビット以上の暗号論的乱数で、レスポンスごとに再生成することです。固定値の使い回しは意味がありません。サーバ側では crypto.randomUUID() などで毎回生成します。

hash 方式(静的なページ向け)

サーバでテンプレートに nonce を差し込めない静的配信では、スクリプト内容の SHA ハッシュを許可リストに書きます。インラインスクリプトの中身から sha256- などのハッシュを計算し、CSP に列挙します。

hash ベースの CSP
Content-Security-Policy: script-src 'sha256-cd9827ad...'; object-src 'none'; base-uri 'none'

外部スクリプトを hash で許可する場合は、<script> 側に同じ値の integrity 属性を付けます。内容が1バイトでも変わればハッシュが変わるため、更新のたびに再計算が必要です。

strict-dynamic の役割

'strict-dynamic' は、「nonce や hash で信頼したスクリプトが動的に読み込む子スクリプトも信頼する」というキーワードです。信頼したローダーが document.createElement('script') で追加するスクリプトに、いちいち nonce を付けなくてよくなります。これにより、多くのサードパーティライブラリを strict CSP と共存させやすくなります。

strict CSP の要点は、script-src の堅牢化に加えて object-src 'none'(プラグイン悪用の遮断)と base-uri 'none'(注入された <base> によるスクリプト読み込み元の乗っ取り防止)を必ずセットにすることです。

レポートによる段階導入(Report-Only)

いきなり本番で強制すると、正規のスクリプトまで止めてサイトを壊しかねません。そこで Content-Security-Policy-Report-Only で「ブロックはせず違反だけ通知」する段階を挟みます。

まずは Report-Only で観測
Content-Security-Policy-Report-Only: default-src 'self'; script-src 'self'; report-to csp-endpoint

レポート送信先は report-to ディレクティブで指定し、実際のエンドポイントは Reporting-Endpoints ヘッダで定義します(Reporting API)。

Reporting API と組み合わせる
Reporting-Endpoints: csp-endpoint="https://example.com/csp-reports"
Content-Security-Policy: default-src 'self'; report-to csp-endpoint

違反が起きると、ブラウザが JSON のレポートを POST します(effectiveDirectiveblockedURL などが入ります)。導入の流れは次のとおりです。

  1. Report-Only でポリシーを流し、違反レポートを収集する
  2. 正規の違反(直すべき自サイトの実装)と、無視してよいノイズを仕分ける
  3. ポリシーを調整し、問題がなくなったら Content-Security-Policy で強制に切り替える

なお、強制用とレポート専用の両方のヘッダを同時に出すこともでき、強制しつつ次の厳しいポリシーを Report-Only で試す運用が可能です。

NOTE

W3C の CSP Level 3(執筆時点で 2026年5月付の W3C Working Draft)では、report-urireport-to へ置き換える形で非推奨(deprecated)と明記されています。ただし report-to はブラウザ対応や送信先定義の都合があるため、当面は両方を併記する運用も見られます。最新の対応状況は MDN で確認してください(この点は「要確認」です)。

よくある落とし穴

  • インラインスクリプト/イベントハンドラが全部止まる: script-src を入れた時点でインラインは既定でブロック。onclick= はイベントリスナーへ、インライン <script> は nonce か hash へ移行する
  • 安易な 'unsafe-inline': 動かすためだけに付けると XSS 防御が実質ゼロになる。'unsafe-inline' は nonce/hash があると無視されるため、モダンブラウザ向けには nonce と併記して後方互換に使う手もある
  • ホスト許可リストへの過信: script-src にドメインを並べる方式はバイパスされやすく、保守も破綻しがち。nonce/hash ベースへ寄せる
  • サードパーティ埋め込みの見落とし: 解析タグや広告、フォント CDN、connect-src で通信する外部 API など、実際に使う読み込み元をレポートで洗い出してから強制する
  • base-uriobject-src の付け忘れ: strict CSP でも、この2つを 'none' にしないと抜け道が残る
  • いきなり強制: Report-Only を挟まず本番強制して、正規機能ごと止める事故が定番

これらは、認証まわりで Cookie・セッション・SameSite を「まず Lax で観測してから締める」のと同じく、観測してから締めるのが安全という点で共通します。

まとめ

  • CSP は「どこから読んだ何を許可するか」をブラウザに宣言する仕組み。主眼は XSS の緩和だが、根絶ではなく多層防御の一層
  • 指定は Content-Security-Policy ヘッダが基本。段階導入には Content-Security-Policy-Report-Only、限定用途で <meta> も可
  • 取得系ディレクティブは default-src にフォールバック。frame-ancestors はクリックジャッキング対策、base-uri / form-action / object-src も要
  • 'unsafe-inline' / 'unsafe-eval' は原則使わない。必要なスクリプトは nonce か hash で個別許可する
  • モダンな指針は nonce + 'strict-dynamic'object-src 'none'base-uri 'none' の strict CSP。ホスト許可リスト方式はバイパスされやすい
  • 導入は Report-Only で観測 → 調整 → 強制の順。レポートは report-to と Reporting API で受ける

CSP は一度に完璧を目指すと必ず折れます。まずは Report-Only で自サイトの実態を知り、少しずつ締めていく。その運用設計こそが、CSP を機能させる最大のコツです。

参考リンク

Cookie・セッション・SameSite の基礎 - Secure / HttpOnly と CSRF・CORS の関係

Cookie・セッション・SameSite の基礎 - Secure / HttpOnly と CSRF・CORS の関係

11

Web認証の土台となる Cookie・セッション・SameSite を実務目線で整理します。Set-Cookie の属性(Expires/Max-Age・Domain/Path)、Secure と HttpOnly が防ぐ脅威、SameSite の Strict/Lax/None の違いと None に Secure が要る理由、サーバー側セッションとログイン後の ID 再生成、SameSite だけで CSRF を防ぎきれない理由、クロスオリジンで Cookie を送る CORS の条件、__Host-/__Secure- プレフィックスまで、MDN・RFC 6265bis・OWASP を一次ソースにまとめます。

CORS の仕組みとハマりどころ - プリフライト・credentials・Allow-Origin を理解する

CORS の仕組みとハマりどころ - プリフライト・credentials・Allow-Origin を理解する

10

CORS(オリジン間リソース共有)を実務目線で整理します。同一オリジンポリシーとの関係、単純リクエストとプリフライト(OPTIONS)の条件、Access-Control-Allow-Origin などの各ヘッダ、credentials 付きで * が使えない理由、Vary: Origin とキャッシュ、そして「No Access-Control-Allow-Origin header」エラーの意味と対処まで、MDN と WHATWG Fetch Standard を一次ソースにまとめます。CORS はブラウザの仕組みであって認可ではない、という勘所も。

Bad Epoll(CVE-2026-46242)とは - Linux カーネル epoll の権限昇格脆弱性と、AI が見逃した競合バグを読み解く

Bad Epoll(CVE-2026-46242)とは - Linux カーネル epoll の権限昇格脆弱性と、AI が見逃した競合バグを読み解く

15

Linux カーネルの epoll サブシステムに見つかった use-after-free 脆弱性 Bad Epoll(CVE-2026-46242)を、NVD などの一次〜準一次情報ベースで整理します。ep_remove() の競合状態からローカルの一般ユーザーが root を奪える仕組み、影響するカーネルバージョンと Android への波及、そして「同じ 2023 年のコミットが混入させた隣の競合バグを Anthropic の Mythos が見つけ、Bad Epoll だけを見逃した」という話題性のある経緯まで、確認できた事実と未確認の点を分けて解説します。