Featured image of post 本番サイトを壊さずにCSPをテストする Content-Security-Policy-Report-Only の運用 Featured image of post 本番サイトを壊さずにCSPをテストする Content-Security-Policy-Report-Only の運用

本番サイトを壊さずにCSPをテストする Content-Security-Policy-Report-Only の運用

Content-Security-Policy-Report-Onlyヘッダーを使い、本番サイトを壊さずにCSPをテストする運用方法を解説。ポリシー違反をレポート収集しながら安全に設定を調整し、XSS対策を段階的に導入する実践的な手法を紹介します。

ウィジェットが壊れる問題

Content Security Policy(CSP)の導入はXSS対策として最も効果的な方法のひとつです。しかし、ディレクティブの設定を一箇所間違えるだけでインラインスクリプトが止まり、CDNリソースがブロックされ、アナリティクスが動かなくなる可能性があります。本番環境にいきなり Content-Security-Policy ヘッダーを適用してクリティカルなスクリプトが遮断されると、サイトは静かに壊れます。

Report-Only モード はこの問題を解決します。

Report-Only と Enforced の違い

CSP には2種類のヘッダーがあります:

ヘッダー動作
Content-Security-Policy違反を即座にブロック
Content-Security-Policy-Report-Only違反をブロックせずレポートのみ送信

Report-Only では、許可されていないスクリプトも実行されますが、ブラウザが JSON 形式の違反レポートを指定のエンドポイントに POST します。本番運用前にすべての誤検出を監査できるのです。

Report-Only の設定

Content-Security-Policy-Report-Only: default-src 'self';
  script-src 'self' https://analytics.example.com;
  report-uri https://your-site.example/csp-reports;
  report-to csp-endpoint;

report-uri ディレクティブ(非推奨だが広くサポート)はレポート先URLを指定します。新しい report-to ディレクティブは Report-To ヘッダーで定義したエンドポイントグループ名を参照します。最大のブラウザカバレッジを得るには両方を併用します。

レポートの受信

違反レポートは次のような JSON ペイロードで送られます:

{
  "csp-report": {
    "document-uri": "https://example.com/page",
    "blocked-uri": "https://evil.com/script.js",
    "violated-directive": "script-src 'self'",
    "effective-directive": "script-src",
    "original-policy": "default-src 'self'; script-src 'self'"
  }
}

レポートの収集方法:

  • 専用サーバールートでファイルやDBにログ出力
  • Report URI(report-uri.com)などのサードパーティサービス
  • AWS S3 + Lambda や Cloudflare Workers によるクラウドログ分析

段階的 CSP ロールアウト戦略

安全な導入は4つのフェーズで構成します。

フェーズ1 — 監視(Report-Only)

バックエンドのトグルやリバースプロキシのルールで、一部のトラフィックにのみ Content-Security-Policy-Report-Only を適用します。1〜2週間 稼働させ、すべての正当なリソースパターンを収集します。

# nginx: ステージング環境でレポートのみ
add_header Content-Security-Policy-Report-Only "default-src 'self'; report-uri /csp-reports;" always;

フェーズ2 — レポートトリアージ

収集したレポートを分析します。よくある誤検出のパターン:

  • インラインイベントハンドラ(onclickonload)は 'unsafe-hashes' または 'nonce-...' が必要
  • script-src に未登録のサードパーティCDNスクリプト
  • connect-src でカバーされていない WebSocket 接続

フェーズ3 — ロックダウン(Enforced)

レポートの発生が落ち着いたら(数日間予期しない違反がゼロになったら)、Enforced ヘッダーに切り替えます。さらに厳しいポリシーを探索するため、Report-Only も併用し続けます。

フェーズ4 — 反復強化

Content-Security-Policy: default-src 'self';
  script-src 'self' 'nonce-abc123';
  report-uri https://your-site.example/csp-reports;

Content-Security-Policy-Report-Only: default-src 'self';
  script-src 'none';
  report-uri https://your-site.example/csp-reports;

Enforced ポリシーと並行して、さらに制限の厳しい将来バージョンを Report-Only でテストできます。

Report-Only の課題

  • レポートはページロードごとに1回のみ送信されます。 最初の違反以降、同じページ上で同じ違反が繰り返されても、ほとんどのブラウザは無視します。
  • ボディなしでレポートを送信するブラウザがあります。 空レポートは早期にフィルタリングしましょう。
  • form-action の違反は Report-Only ではキャッチできません。 フォーム送信先の制限は Enforced モードでなければブロックされません。

まとめ

Content-Security-Policy-Report-Only は、リスクゼロで CSP をロールアウトするための必須ツールです。本番トラフィックで違反を観察し、実際のデータに基づいてポリシーを調整し、確信が持てた段階で Enforced モードに移行できます。レポート収集サービスと組み合わせて、最低1週間は運用してから本適用を行いましょう。