Webサイトのコンテンツセキュリティポリシー:CSPの設定・バイパスリスク・導入

CSP(コンテンツセキュリティポリシー)は、どのスクリプトを実行し、どのドメインからリソースを読み込めるかをブラウザに伝えるレスポンスヘッダーです。導入は必ず3段階で進めます。まずReport-Onlyで14日間運用して違反元を収集し、次にホワイトリストを整理して、

CSP部署:报告模式先跑两周再切强制CSP: report-only for two weeks, then enforce 一上来就enforce会把站内第三方脚本全打挂Enforcing on day one breaks every third-party script 1 Report-Only1 Report-Only 只收集违规 不拦截Collect violations, no block 跑14天 收集所有来源Run 14 days to collect sources 2 白名单收敛2 Whitelist tuning 按报告加域名 不violent-inlineAdd domains from reports sha256 hash 替代unsafe-inlinesha256 instead of unsafe-inline 3 Enforce3 Enforce 违规日志为0再切Switch when reports hit 0 保留report-uri 继续监控Keep report-uri watching 核心指令Core directives default-src 兜底 script-src 控脚本default-src fallback, script-src controls JS object-src ‘none’ frame-ancestors 防嵌套object-src none, frame-ancestors 红线Red lines 别开 * 通配符 等于没配Wildcard * equals no CSP unsafe-inline 留着等于被绕过unsafe-inline defeats the purpose 判定:报告模式跑满14天且违规数降到个位数,再切EnforceDecision: enforce after 14 days of near-zero reports 反直觉:CSP不是越严越安全,配错了比不配更危险Counterintuitive: misconfigured CSP is worse than none 先报告后强制,把违规源一个个收进白名单Report first, enforce later
CSPの3段階導入:Report-Onlyで収集、ホワイトリストを整理、Enforceで強制

Webサイトのコンテンツセキュリティポリシー:CSPの設定・バイパスリスク・導入

結論から言うと:CSP(コンテンツセキュリティポリシー)は、どのスクリプトを実行し、どのドメインからリソースを読み込めるかをブラウザに伝えるレスポンスヘッダーです。導入は必ず3段階で進めます。まずReport-Onlyで14日間運用して違反元を収集し、次にホワイトリストを整理し、違反ログが1桁台になってからEnforceに切り替えます。直感に反する判断ですが、設定を誤ったCSPは未設定より危険です。unsafe-inlineを有効にすると、この壁を築いた意味がなくなります。

1. CSPが実際に防ぐもの

CSPはHTTPレスポンスヘッダーでルールを配信し、ブラウザはそれに従ってどのリソースを読み込めるか判断します。主に2つの問題を防ぎます。XSSで注入された悪意あるスクリプトと、許可されていない第三者ドメインがページに埋め込まれることです。

これはファイアウォールではありません。CSPはSQLインジェクションもサーバーへの侵入も防げません。管理するのはブラウザ側のリソース読み込みです。HTTPSやHSTSとは連携する関係で、HTTPSの設定方法は独立サイトのHTTPS設定。

独立サイトでよくある悩みは第三者スクリプトの多さです。GA4、GTM、Hotjar、チャットサポート、広告ピクセルなど。スクリプトを追加するたびにCSPのホワイトリストにドメインを1つ足さないと、ブラウザにブロックされます。第三者スクリプトの管理は独立サイトの第三者スクリプト管理。

見落とされがちな事実として、CSPはSEOにほとんど悪影響を与えません。Googlebot自体はJS以外のリソース制限を実行しないため、CSPを正しく設定してもインデックスに影響しません。CSPがクロールを妨げるという心配は不要です。

ディレクティブ 管理対象 推奨値
default-src 全リソースの受け皿 ‘self’
script-src JSスクリプトの取得元 ‘self’ + 解析ドメイン
style-src CSSの取得元 ‘self’ ‘unsafe-inline’
img-src 画像の取得元 ‘self’ data: + CDN
object-src プラグイン ‘none’(必ず無効化)
frame-ancestors 本站を埋め込めるサイト ‘none’

2. 3段階の導入:レポート、整理、強制

最もよくある間違いは、いきなりContent-Security-Policyヘッダーを付けることです。これにより、ホワイトリストにないサイト内の全スクリプトが即座にブロックされ、GA4、チャットサポート、ピクセルがすべて停止し、担当部署からすぐ苦情が来ます。

正しい方法は、まずContent-Security-Policy-Report-Onlyヘッダーを使うことです。ブラウザは違反を報告するだけでブロックせず、2週間ログを収集して、実際に使われている全ドメインをホワイトリストに追加します。

シナリオ例1:WordPressサイトにCSPを追加し、まずReport-Onlyを配信します。

2日後のレポートにgoogletagmanager.com、hotjar.com、youtube.comの3つのドメインが現れます。

1つずつscript-srcとframe-srcに追加します。2週間後、レポートが1日1桁台になれば強制モードに切り替えます。

フェーズ レスポンスヘッダー 行為 期間
ステップ1 Content-Security-Policy-Report-Only レポートのみ、ブロックなし 14日間
ステップ2 同様に、段階的にホワイトリストを追加 違反は日ごとに減少 一桁台になるまで
ステップ3 Content-Security-Policy 実際にブロック 長期
全期間 report-toまたはreport-uriを追加 違反レポートを収集 常時有効

インラインスクリプトが最大の落とし穴です。WordPressのテーマや多くのプラグインはページ内に直接<script>を書き込むため、CSPはデフォルトでインラインをブロックします。対処法は2つ。1つはインラインスクリプトを外部jsファイルに切り出す方法、もう1つはsha256ハッシュで該当部分だけを許可する方法です。安易にunsafe-inlineを有効にするのは避けてください。

style-srcはscript-srcより緩めで構いません。Gutenbergエディタや多くのテーマはインラインstyleを出力するため、style-srcに一時的にunsafe-inlineを残しても問題は小さいです。

ただしscript-srcでunsafe-inlineを有効にするのは絶対に避けてください。ここがCSPによるXSS対策の核心です。2つのディレクティブは緩さの基準を分けて考える必要があります。

三、回避リスク:CSPを形骸化させない

CSPは設定を誤ると意味がありません。以下の3つの書き方でこの壁は無効化されます。

1つ目はunsafe-inlineの有効化です。あらゆるインラインスクリプトの実行を許すため、XSSで注入された悪意あるスクリプトも動き、CSPの核心が破られます。2つ目はワイルドカード*の指定で、これは配信元を制限しないのと同じです。3つ目はobject-srcの漏れで、古いブラウザではFlashやプラグイン経由で回避されます。

シナリオ例2:手軽さを優先してscript-srcに’unsafe-inline’を書いてしまった。

その2か月後、サイトにインラインのフィッシングスクリプトを注入され、CSPはまったく防げませんでした。

正しい方法はnonceの使用です。リクエストごとに使い捨ての乱数を生成し、その値を持つインラインスクリプトだけを許可します。

誤った書き方 リスク 代替案
script-src * 任意のサードパーティスクリプトが読み込める ホワイトリストのドメインを個別に列挙
script-src ‘unsafe-inline’ XSSで注入されたスクリプトがそのまま実行される 外部ファイルに切り出すかnonce/ハッシュを使う
object-srcを書かない 古いプラグインが悪用されうる object-src ‘none’
frame-ancestorsを設定しない ページが悪意あるiframeに埋め込まれる frame-ancestors ‘none’

CSPとコンプライアンスは別物です。GDPRのCookie同意はまた別の仕組みで、詳しくは独立サイトのGDPR対応をご覧ください。CSPが防ぐのはスクリプト注入であり、Cookie同意の問題は解決しません。サーバー側のログインとファイル権限については独立サイトのセキュリティ強化。

nonceとハッシュはどう使い分ける?固定のインラインスクリプトにはハッシュ、動的に生成されるインラインスクリプトにはnonceを使います。WordPressテーマ内の固定のトラッキングコードはハッシュで許可し、リクエストごとに内容が変わるスクリプトブロックにはnonceを使います。

配信場所にも工夫が必要です。CSPレスポンスヘッダーはNginxやCDN層で配信し、PHPアプリからの出力に頼らないようにしてください。アプリ層の出力はPHP障害時に失われますが、CDN層からの配信は安定しています。

配置場所 安定性 適用シーン
Nginx設定 高、アプリが落ちても残る 自前サーバー
CDNルール 高、全世界で有効 Cloudflareなどを利用
PHPアプリ出力 低、障害時に失われる 一時的なテスト

レポート受信エンドポイントも専用に用意してください。report-uriが指すアドレスをCSP自身がブロックすると、レポートを送信できなくなります。

独立系サイトでは既存のレポート収集サービスを使うか、Nginxに専用のlog endpointを設けて報告を受け取り、別のログファイルに書き出します。本体サイトのトラフィックログには残しません。

報告が増えたら集計が必要です。1日に数百件届くこともあり、1件ずつ見るのは無理なので、違反したディレクティブとブロックされたドメインでグループ化し、毎日新しく増えたドメインを重点的に確認します。

よくある誤解があります。CSPを入れたら安心だと思うことです。実際には、サードパーティスクリプトを追加するたび、テーマを更新するたびに、新しい違反元が入り込む可能性があります。

そのためEnforceに切り替えたあとも、report-uriは外せません。新しく追加したスクリプトのホワイトリスト登録を忘れたことに気づける唯一の信号源です。

ブラウザ対応にも触れておきます。モダンブラウザ(Chrome、Edge、Firefox、Safariの直近2年分)はCSP 3に完全対応しています。古いIEは無視して構いません。B2Bの独立系サイトの訪問者がIEを使うことはまずありません。

もう1つデプロイの注意点があります。CSPヘッダーはhttpとhttpsの両方に送る必要があります。httpsのみに送ると、httpでオリジンに戻る段階のリクエストがCSPの保護を受けません。

Enforceに切り替えたあとは、Report-Onlyヘッダーを並行してしばらく残すことをおすすめします。ブロックしつつ報告も受け取れるので、漏れがあってもレポートで確認できます。

CSPは一度きりのプロジェクトではありません。新しいサードパーティスクリプト(チャット、動画、広告など)を追加するたびに、まずレポートモードで違反として現れるのを確認し、該当ドメインをホワイトリストに追加してから本番に切り替えます。

CSPチェックを新規スクリプトのリリースチェックリストに入れましょう。スクリプトを追加すると同時にホワイトリストも追加し、リリース後にユーザーから「部品が表示されない」と報告されてからCSPを思い出す、という事態を避けます。

1つの判断基準があります。あるサードパーティスクリプトが外部ファイル化もハッシュ計算もできない場合、そのサードパーティを使う価値があるかを見直すべきです。unsafe-inlineに依存するサードパーティサービス自体が技術的負債です。

デプロイ完了後は、ブラウザのコンソールのSecurityパネルでページを1つずつ確認します。トップページ、商品ページ、ブログページ、フォームページをそれぞれ見て、赤い違反表示がないことを確認します。

確認で問題があったページは、レポートから該当ドメインを探し、ホワイトリストに追加してもう1日観察します。このサイクルを2〜3回繰り返せば安定します。

CSPは一度デプロイすれば終わりのセキュリティ項目ではありません。サードパーティスクリプトのライブラリとともに長期的に進化します。新しいスクリプトを追加するたびにホワイトリストも更新し、新たに現れたサードパーティスクリプトのドメインを登録し忘れないようにしてください。

レポートの発生元 意味 対処
script-src違反 新規スクリプトが未登録 ドメイン追加か外部ファイル化
img-src違反 画像の外部リンクが新しいドメイン CDN追加か相対パスに変更
frame-src違反 新しいiframeを埋め込み 動画ドメインを追加か削除
base-uri違反 ページにbaseタグを注入 すぐにXSSを調査

FAQ よくある質問

CSPは公開時から強制モードにすべき?
不要。まずContent-Security-Policy-Report-Onlyのレポートモードで14日間運用し、ブラウザには違反を報告させるだけでブロックはさせません。レポートに出た実ドメインを1つずつホワイトリストに追加し、違反ログが1桁まで減ってからContent-Security-Policyの強制モードに切り替えます。いきなり強制するとGA4、チャットサポート、広告ピクセルが全部止まります。
WordPressテーマのインラインスクリプトがCSPでブロックされたら?
安易にunsafe-inlineを有効にしてはいけません。正しい方法は2つ。1つはインラインスクリプトを独立したjsファイルに切り出してsrcで読み込む。もう1つは残す必要のあるインラインスクリプトのsha256ハッシュを計算し、script-srcでそのハッシュだけを許可する。unsafe-inlineを許すとXSSで注入された悪意あるスクリプトまで実行できてしまい、CSPを設定した意味がなくなります。
CSPとHTTPS、HSTSはどう関係しますか?
3つは連携しますが役割は重なりません。HTTPSは通信の暗号化、HSTSはブラウザにHTTPSのみを使わせる仕組み、CSPはブラウザ側で読み込めるスクリプトやリソースを制御します。HTTPSの導入が前提で、CSPはその上でXSSや未承認の第三者スクリプトを防ぎます。CSPだけ入れてHTTPSがないのは無意味です。
CSPレポートが違反だらけになるのはなぜ?
ホワイトリストが不完全だからです。違反レポートにはブロックされたスクリプトのドメインが記録されるので、そのドメインを該当するディレクティブ(script-src、img-src、frame-srcなど)に追加します。第三者統計、チャット、動画埋め込みが最もよく漏れます。レポートモードの意味はここにあり、まずきれいに集めてから強制します。
CSPはSQLインジェクションを防げますか?
防げません。CSPはブラウザ側で動き、リソースの読み込みを制御するだけで、サーバー側のSQLインジェクションは防げません。SQLインジェクションはデータベースのプリペアドステートメントと入力フィルタで対処します。CSPが主に防ぐのはXSSで、攻撃者が注入した悪意あるスクリプトをCSPがブロックして実行させませんが、注入自体はサーバーで起きています。
report-uriはまだ残す必要がありますか?
必要です。Enforceの強制モードに切り替えても、report-toまたはreport-uriを残して新たな違反を継続的に収集します。新しく追加した第三者スクリプトやテーマ更新で入ったインラインコードは、まずレポートに現れます。違反報告をログサービスに送り、毎日異常なドメインを確認するのがおすすめです。

最後に

CSPの難しさはルールを書くことではなく、根気よくレポートモードを運用することにあります。2週間の観察期間で、その後数年のスクリプトを制御できるのですから、割に合います。

導入は3ステップ。まずReport-Onlyで14日間運用。レポートに従って実ドメインを1つずつホワイトリストに入れ、unsafe-inlineをハッシュで置き換える。違反が1桁まで減ったら強制に切り替え、同時にreportを残して監視を続けます。

覚えておきたい一言。CSPの安全度はホワイトリストがどれだけきれいかで決まります。unsafe-inlineやワイルドカードを残せば、この壁は築いていないのと同じです。

环形山
环形山 クレーター専門家

公式アカウント · 環形山 Crater

環形山(Crater)は、WordPress独立サイト開発+Google SEO+GEO全域成長に特化した海外展開支援会社で、スマートハードウェア、産業機器、SaaS、新エネルギーなどの分野を手がけています。

プロダクト能力方法論ホワイトペーパー業界レポート
965件のシェア
78件のナレッジベース