【実務・中級編】Content-Security-Policy (CSP) の厳密なディレクティブ設計 – アプリケーションセキュリティ & 安全な開発防御ガイド

CSP(Content-Security-Policy)は「魔法の杖」ではない。防御の最後の砦を築くための設計論

現場でインシデント対応をしていると、よくこんな相談を受ける。「CSPを入れたいんですが、どのディレクティブを許可すればいいですか? `unsafe-inline` を消すと画面が真っ白になるんです」と。

ハッキリ言おう。`unsafe-inline` を許可したCSPは、鍵をかけた玄関のドアに「合鍵をマットの下に隠しました」と張り紙をしているようなものだ。攻撃者はそこを狙う。今回は、XSSを根絶するために、現場で「本当に効く」CSPの設計思想と実装について話そう。

1. なぜ `unsafe-inline` が悪なのか?(PoCのリスク)

攻撃者は、アプリケーションの脆弱性(DOM-based XSSなど)を見つけると、必ず以下のペイロードを試す。

もしCSPで `script-src ‘unsafe-inline’` が許可されていれば、このスクリプトはブラウザで即座に実行される。WAFでフィルタリングしていても、難読化やエンコードを駆使されると、防ぎきれないケースが後を絶たない。CSPは「ブラウザ側で実行を拒否する」という強力な強制力を持つ。これを使わない手はない。

2. 厳密なCSP設計の要:nonce(ナンス)の活用

インラインスクリプトを安全に使うための唯一の正解は `nonce`(Number used ONCE) だ。リクエストごとに使い捨てのランダムな文字列を生成し、サーバーとHTMLの両方で照合する。

実装例:PHPでnonceを生成し、CSPヘッダーを送信する

タグの悪用によるパスハイジャックを防止
header(“Content-Security-Policy: ” .
“default-src ‘self’; ” .
“script-src ‘nonce-$nonce’ ‘strict-dynamic’ https:; ” .
“object-src ‘none’; ” .
“base-uri ‘self’;”);
?>









なぜ `strict-dynamic` を組み合わせるのか

近年のモダンな構成では、ライブラリが動的に子スクリプトをロードすることが多い。`strict-dynamic` を付けることで、「信頼されたスクリプトが生成したスクリプト」を自動的に信頼させることができる。これが実務上、開発体験を損なわないための最適解だ。

3. インフラレベルでの防御(Nginx設定)

アプリケーションコードを変更できないレガシーな環境であっても、NginxなどのリバースプロキシでCSPを付与することは可能だ。ただし、この場合はnonceが使えないため、ハッシュ値ベースの運用を検討する必要がある。

Nginxの設定例
add_header Content-Security-Policy “default-src ‘self’; script-src ‘self’ ‘sha256-xyz…’; object-src ‘none’; base-uri ‘self’;”;

※ `sha256-xxxx` は実行させたいスクリプトの中身をハッシュ化したもの。静的なファイルには有効だが、動的な生成には向かない。

4. 現場で「詰まない」ためのデバッグTips

CSPをいきなり「厳密モード」で本番投入するのは、爆弾を抱えて走るようなものだ。まずは `Content-Security-Policy-Report-Only` ヘッダーを使え。

これを使うと、ポリシーに違反してもブロックはされず、違反内容が指定したエンドポイントにJSONとして送信される。

報告用エンドポイントのJSON例:

{
“csp-report”: {
“blocked-uri”: “https://attacker.com/malicious.js”,
“violated-directive”: “script-src”,
“original-policy”: “default-src ‘self’; …”
}
}

まずは3日間ほどレポートモードで運用し、正当なトラフィックがどこでブロックされているかをログ分析ツール(SentryやDatadogなど)で可視化する。そこから一つずつ除外リストを整理していくのが、プロのインシデントハンドリングだ。

最後に:防御は「穴」を塞ぐ作業ではない

セキュリティは「リスクのコントロール」だ。CSPを導入したからといって、アプリケーション側の脆弱性が消えるわけではない。しかし、CSPは「脆弱性が存在しても、攻撃を成功させない」という、極めて強力な多層防御を実現する。

「面倒だから」と `unsafe-inline` を放置するのではなく、まずは `report-only` から始めてほしい。あなたの書いたコードが、翌日のインシデントニュースにならないように。現場からは以上だ。

コメント

タイトルとURLをコピーしました