結論(Content Security Policy)
Content Security Policyとは、ページがどこからスクリプト・スタイル・画像などを読み込み、どのような実行を許すかをブラウザに宣言させる仕組みです。XSSで攻撃者のスクリプトが紛れ込んでも、許可していない配信元からの読込やインライン実行をブラウザが拒否するため、被害を抑える「最後の防御線」になります。CSPがあるだけでは不十分で、スクリプトの読込元を制限する script-src または default-src を含めることが必要です。
この記事のポイント
- CSPはXSSの根本対策(出力時のエスケープ等)の代わりではなく、対策が漏れた場合の被害軽減策です。
- 配信方法はレスポンスヘッダー(推奨)、meta要素、Report-Onlyヘッダーの3通りで、効果が異なります。
- script-src も default-src もないCSP(upgrade-insecure-requestsだけ等)は、スクリプトを何も制限しません。
- セキュリティAIチェッカーは、CSPがありscript-srcかdefault-srcを含めば○、CSPはあるが両方ないと△、CSPがなければ×と判定します。
- 導入は棚卸し→Report-Onlyで試行→強制→厳格化の順に段階的に進めるのが安全です。
| 診断項目名 | Content Security Policy |
|---|---|
| 項目ID | headers.csp |
| 分類 | コンテンツセキュリティポリシー(CSP) |
| 重大度・配点の重み | 重要(high) 重み 4 |
| 関連する標準・ガイドライン | OWASP ASVS 5.0.0 V3.4.3(L2)、OWASP WSTG-CONF-12(Test for Content Security Policy)、IPA「安全なウェブサイトの作り方」1.5 クロスサイト・スクリプティング(保険的対策)、CWE-693(保護メカニズムの不備)、CWE-79(多層防御として) |
Content Security Policy(CSP)とは
**Content Security Policy(CSP、コンテンツセキュリティポリシー)**とは、ページがどこからスクリプト・スタイル・画像などを読み込み、どのような実行を許すかを、ブラウザに宣言させる仕組みです。サーバーは Content-Security-Policy レスポンスヘッダーなどでポリシー(許可のルール)を送り、ブラウザはそのルールに反する読込や実行を拒否します。
Content-Security-Policy: default-src 'self'; script-src 'self' https://cdn.example.jp; object-src 'none'; base-uri 'self'; frame-ancestors 'self'
XSS(クロスサイト・スクリプティング)で攻撃者のスクリプトがページに紛れ込んでも、許可していない配信元からの読込やインライン実行をブラウザが拒否するため、CSPは被害を抑える「最後の防御線」になります。
ただし、CSPは出力エスケープなどの根本対策の代わりではありません。根本対策が漏れた場合の被害軽減策です。セキュリティAIチェッカーのこの項目では、トップページの応答にCSPが存在し、かつスクリプトの読込元を実際に制限するディレクティブ(script-src または default-src)を含んでいるかを確認します。
CSPの配信方法は3通り
| 配信方法 | 効果 | 注意点 |
|---|---|---|
Content-Security-Policy レスポンスヘッダー | 強制(推奨) | すべてのディレクティブが有効 |
meta http-equiv="Content-Security-Policy" 要素 | 強制 | frame-ancestors・report-uri・sandbox は無視される |
Content-Security-Policy-Report-Only ヘッダー | 違反を報告するだけで遮断しない | meta要素では使えない |
W3C CSP Level 3 は、meta要素で配信したポリシーもヘッダーのポリシーと併せて強制されると定めています。複数のポリシーが有効な場合、リソースはすべてのポリシーで許可されたときだけ読み込まれます。つまり、ポリシーを追加すると制限は強くなる一方で、緩くはなりません。
CSPヘッダー値の構造
- 1つのポリシーは
;区切りのディレクティブの列です(例:default-src 'self'; script-src 'self')。 - 複数のポリシーは
,で区切ります。 - サーバーが
Content-Security-Policyヘッダーを2本送ると、HTTPの仕様上、カンマで連結された1つの値として扱われます。結果として2つのポリシーが両方とも適用されます。
なぜ「CSPがある」だけでは不十分なのか
スクリプトを制限していないCSPがある
CSPの各フェッチディレクティブ(script-src、img-src など)は、指定がなければ default-src にフォールバックし、default-src もなければ無制限です。
たとえば次のようなポリシーは、HTTPS化やクリックジャッキング対策としては有効ですが、スクリプトに対しては何も制限しません。
Content-Security-Policy: upgrade-insecure-requestsContent-Security-Policy: frame-ancestors 'self'
Mozilla HTTP Observatory も、script-src/object-src の配信元を制限していないCSPを「安全でない実装」として減点しています。そのためセキュリティAIチェッカーは、CSPの有無だけでなく、script-src または default-src があるかまで確認しています。
Content Security Policyについてよくある誤解
- 「CSPを入れたのでXSSは起きない」:
'unsafe-inline'を許可したCSPは、反射型・格納型XSSの典型的な注入(<script>...</script>)を止められません。この点は「CSPでunsafe-inlineを許可していない」で評価します。 - 「metaで書けばヘッダーと同じ」:meta CSPは
frame-ancestorsを強制できず、また<meta>より前に出力された内容には適用されません。 - 「Report-Onlyで導入済み」:Report-Only は遮断しないため、防御としては未導入と同じです。強制かどうかは「CSPがレスポンスヘッダーで強制されている」で評価します。
CSP関連の診断項目との分担
このページは「CSPがあり、スクリプトを制限しているか」に絞って解説します。CSPの中身の安全性は、次の項目でそれぞれ確認します。
| 確認したいこと | 診断項目 |
|---|---|
| 強制ヘッダーか、Report-Only/metaか | 「CSPがレスポンスヘッダーで強制されている」 |
object-src・base-uri・form-action・frame-ancestors の有無 | 「CSPの基幹ディレクティブ」 |
| インラインスクリプトの許可 | 「CSPでunsafe-inlineを許可していない」 |
| 文字列のコード実行(eval)の許可 | 「CSPでunsafe-evalを許可していない」 |
| 既定値(default-src)の有無 | 「CSPのdefault-src」 |
* や https: などの広すぎる許可 | 「CSPの過剰なワイルドカード」 |
| 平文HTTPの配信元の許可 | 「CSPでHTTP配信元を許可していない」 |
| 違反レポートの送信先 | 「CSP違反の報告先」 |
Content Security Policyの診断基準(セキュリティAIチェッカー)
取得対象
- 診断URLへGETリクエストを送り(リダイレクトは最大4回まで追跡、本文は最大2MB、タイムアウト15秒)、最終応答のヘッダーとHTMLを調べます。
Content-Security-Policyレスポンスヘッダーを取得します。複数本ある場合はカンマ連結値として取得します。- HTML内の
meta http-equiv="Content-Security-Policy"要素のcontent属性も取得します。引用符の種類を対応付けて抽出するため、値の中の'self'などのシングルクォートで途切れません。値が複数行にわたっていても閉じ引用符まで読み取ります。 http-equivの値が引用符で囲まれていないmeta、および属性値に<や>を含むmetaは検出しません。- ヘッダーとmetaの両方がある場合、根拠欄に表示する値はヘッダーを優先し、判定はヘッダーとmetaの全ポリシーを対象にします。
判定条件
| 判定 | 条件 | 得点への影響 |
|---|---|---|
| ○ 問題なし(pass) | CSP(ヘッダーまたはmeta)があり、いずれかのポリシーに script-src または default-src がある | 重み4を満点計上 |
| △ 要確認(warn) | CSPはあるが、どのポリシーにも script-src・default-src がない(スクリプトを制限していない) | 4×0.45=1.8点 |
| × 要改善(fail) | CSPがヘッダーにもmetaにもない | 0点 |
| 対象外(na) | なし(この項目は常に判定します) | — |
- 総合スコアは「Σ(重み×係数)÷Σ(適用項目の重み)×100」で計算します(係数は○=1.00、△=0.45、×=0)。
- この項目では、
'unsafe-inline'や*などの中身の弱さは減点しません。中身の弱さは前の表の個別項目で減点します。
自治体サイトでの判定例
| 応答 | 判定 |
|---|---|
| CSPヘッダーなし、metaもなし | × |
Content-Security-Policy: upgrade-insecure-requests のみ | △ |
Content-Security-Policy: frame-ancestors 'self' のみ | △ |
default-src 'self' 'unsafe-inline' https: | ○(中身の弱さは個別項目で減点) |
meta CSPで script-src 'self' https://www.googletagmanager.com | ○(ただし「CSPがレスポンスヘッダーで強制されている」は△) |
Content Security Policyがないとどんなリスクがあるか
- XSSの被害拡大:CMSのテンプレートや問い合わせフォームの確認画面などにエスケープ漏れがあると、攻撃者は任意のスクリプトを実行できます。CSPがなければ、外部ドメインからの攻撃スクリプトの読込、入力内容の外部送信、偽ログイン画面の表示などを止める仕組みがありません。
- 改ざん・サプライチェーン攻撃:外部の解析タグやウィジェットの配信元が侵害された場合でも、CSPで許可していない配信元への通信は遮断できます。CSPがなければ、この歯止めもありません。
- 住民の信頼失墜:自治体公式サイト上で偽の給付金案内やフィッシング誘導が表示されると、行政への信頼が大きく損なわれます。
CSPは被害軽減策であり、根本対策(出力時のエスケープ、テンプレートエンジンの自動エスケープ、入力値検証)と併せて実施することが前提です。
Content Security Policyの設定方法(Apache・nginx・IIS・Next.js)
サイトを壊さない段階的な導入手順
- 棚卸し:トップページと主要ページで読み込んでいるスクリプト・スタイル・画像・フォント・iframe・通信先(解析タグ、地図、動画、翻訳、SNS)を一覧にします。
- Report-Onlyで試行:まず
Content-Security-Policy-Report-Onlyで配信し、数週間、違反レポートやブラウザの開発者ツールのコンソールを確認します。 - 強制へ切り替え:違反がなくなったら、同じ値を
Content-Security-Policyで配信します。Report-Onlyは、次の厳格化の試験用として並行運用できます。 - 厳格化:
'unsafe-inline'の除去(nonce/hash化)、ワイルドカードの縮小を順に進めます。
Apache(.htaccess)
<IfModule mod_headers.c>
# まずは試行(遮断しない)
Header always set Content-Security-Policy-Report-Only "default-src 'self'; script-src 'self' https://www.googletagmanager.com; object-src 'none'; base-uri 'self'; frame-ancestors 'self'"
# 検証後に強制へ
# Header always set Content-Security-Policy "default-src 'self'; script-src 'self' https://www.googletagmanager.com; object-src 'none'; base-uri 'self'; form-action 'self'; frame-ancestors 'self'"
</IfModule>
IfModuleで囲み、mod_headers が無効な環境で500エラーにならないようにします。.htaccessは配下のディレクトリにも継承されます。インライン前提の古いアプリが配下にある場合は、そのフォルダだけ別の.htaccessで値を上書きします。
nginx
add_header Content-Security-Policy "default-src 'self'; script-src 'self'; object-src 'none'; base-uri 'self'; frame-ancestors 'self'" always;
add_header は、下位の location で別の add_header を書くと、上位の設定が継承されなくなる点に注意してください。
IIS(web.config)
<system.webServer>
<httpProtocol>
<customHeaders>
<add name="Content-Security-Policy" value="default-src 'self'; script-src 'self'; object-src 'none'; base-uri 'self'; frame-ancestors 'self'" />
</customHeaders>
</httpProtocol>
</system.webServer>
Next.js(Vercel)
nonceを使わない場合は next.config.js の headers() で設定します。nonceを使う場合は proxy.ts(旧middleware)で毎リクエスト生成します。nonce方式では全ページが動的レンダリングになる点に注意してください(Next.js公式ガイド)。
// next.config.js(nonceなし)
const csp = "default-src 'self'; script-src 'self'; object-src 'none'; base-uri 'self'; form-action 'self'; frame-ancestors 'none'; upgrade-insecure-requests";
module.exports = {
async headers() {
return [{ source: '/(.*)', headers: [{ key: 'Content-Security-Policy', value: csp }] }];
},
};
CMS(WordPress等)を利用している場合
WordPress等ではプラグインやテーマがインラインスクリプトを多用するため、いきなり厳格なCSPを強制すると表示が崩れます。サーバー設定(.htaccess/nginx)でReport-Onlyから始め、管理画面(/wp-admin/ 等)は別のポリシーにするのが安全です。
設定後の確認方法
curl -sI https://www.example.lg.jp/ | grep -i '^content-security-policy'
curl -s https://www.example.lg.jp/ | grep -io '<meta[^>]*content-security-policy[^>]*>'
1行目でレスポンスヘッダーのCSP、2行目でHTML内のmeta CSPを確認できます。あわせて、ブラウザの開発者ツールのコンソールに「Refused to load ...」「Refused to execute inline script ...」が出ていないかも確認します。表示が崩れている場合は、そのメッセージに出ている配信元やインラインコードを許可リストに加えるか、外部ファイル化します。
設定を変えたら、セキュリティAIチェッカーで再診断し、この項目が○になっていること、あわせて前述の個別項目(unsafe-inline、ワイルドカードなど)の判定も確認してください。
よくある質問(FAQ)
Content Security Policy(CSP)を入れればXSSは起きなくなりますか?
起きなくなるわけではありません。CSPは出力エスケープなどの根本対策が漏れた場合に被害を抑える策です。特に 'unsafe-inline' を許可したCSPは、典型的な script タグの注入を止められません。
CSPはmetaタグで書いてもヘッダーと同じ効果がありますか?
同じではありません。meta要素のCSPも強制されますが、frame-ancestors・report-uri・sandbox は無視され、meta要素より前に出力された内容には適用されません。レスポンスヘッダーでの配信が推奨です。
Content-Security-Policy-Report-Onlyだけを設定していれば導入済みですか?
Report-Onlyは違反を報告するだけで遮断しないため、防御としては未導入と同じです。強制ヘッダーかどうかは「CSPがレスポンスヘッダーで強制されている」(headers.csp-enforcement)で評価します。
upgrade-insecure-requestsだけのCSPではだめですか?
HTTPS化の補助としては有効ですが、スクリプトに対しては何も制限しません。フェッチ系ディレクティブは指定がなければ default-src に、default-src もなければ無制限になるためです。セキュリティAIチェッカーでは△(要確認)になります。
CSPヘッダーを2本送るとどうなりますか?
HTTPの仕様上、カンマで連結された1つの値として扱われ、複数のポリシーがすべて適用されます。リソースはすべてのポリシーで許可されたときだけ読み込まれるため、ポリシーを追加すると制限は強くなる一方です。
CSPを導入するとサイトが壊れませんか?
いきなり強制すると、許可していない外部タグや地図・動画が止まることがあります。まずReport-Onlyで数週間違反を確認し、違反がなくなってから同じ値を強制ヘッダーで配信すれば、表示を壊さずに導入できます。
WordPressなどのCMSでもCSPを設定できますか?
設定できますが、プラグインやテーマがインラインスクリプトを多用するため、厳格なCSPをいきなり強制すると表示が崩れます。サーバー設定でReport-Onlyから始め、管理画面は別ポリシーにするのが安全です。
この項目を含む127項目を、URLを入れるだけで無料診断します。
参考資料
- W3C Content Security Policy Level 3
- OWASP ASVS 5.0 V3 Web Frontend Security
- OWASP WSTG-CONF-12 Test for Content Security Policy
- OWASP Content Security Policy Cheat Sheet
- Mozilla HTTP Observatory Scoring
- MDN HTTP Observatory CSPテスト実装(csp.js)
- MDN CSP: script-src
- Next.js「Content Security Policy」ガイド
- IPA「安全なウェブサイトの作り方」