現場で生き残るための「CSP nonce/hash」実装ガイド:インラインスクリプトを安全に封じ込める
現場でインシデント対応をしていると、いまだに「CSP(Content Security Policy)を入れるとサイトが壊れるから」という理由で導入を見送るチームに出くわす。しかし、今の時代、XSS(クロスサイトスクリプティング)を単なる「アラートが出る脆弱性」と侮っていると、バックエンドの機密情報を根こそぎ抜かれることになる。
今日は、攻撃者が最も好む「インラインスクリプトの注入」を、CSPのnonceとhashを使って、運用を止めずに徹底的に封じ込める術を教える。
—
1. なぜ「インラインスクリプト」が狙われるのか
攻撃者は、あなたの書いた洗練されたJavaScriptファイルを改ざんする必要はない。彼らは、HTMLのテンプレートに直接埋め込まれたタグの隙間を狙う。
例えば、ユーザーの入力値がそのまま画面に反映される箇所があれば、こんなPoCが飛んでくる。
単純だが、これが実行された瞬間にセッションハイジャックが成立する。これを防ぐには「信頼できないスクリプトを一切実行させない」ポリシーを強制する必要がある。それがCSPだ。
---
2. nonce(ワンタイムトークン)による防御の実装
nonce(Number used once)は、リクエストごとに生成されるランダムな文字列だ。サーバー側で生成し、CSPヘッダーとHTMLタグの両方に同じ値を埋め込む。ブラウザは「ヘッダーの値」と「タグのnonce属性」が一致した場合のみ、そのスクリプトを実行する。
実装例:PHPで動的にnonceを生成する場合
まずは、サーバー側でセキュアな乱数を生成し、ヘッダーとテンプレートに渡す仕組みを作る。
この実装の肝は、header関数でnonceを動的に生成し、HTML側と同期させる点にある。攻撃者はHTMLにスクリプトを埋め込めたとしても、その瞬間の正しいnonceを知り得ないため、実行権限を得ることができない。
---
3. 静的なスクリプトなら「hash」を使う
動的に生成できない静的なスクリプトブロック(例えば、設定値だけを埋め込んだ小さなスクリプト)には、hashが有効だ。スクリプトの中身をSHA-256などでハッシュ化し、それをCSPヘッダーに登録する。
設定例:Nginx/ApacheでのCSPヘッダー定義
もしスクリプトの内容が固定なら、ヘッダー側で許可を与えることができる。
Nginxの例:ハッシュ値で特定のインラインスクリプトを許可
add_header Content-Security-Policy "script-src 'sha256-RkH7...(ここにハッシュ値)...=';";
- 注意点: ハッシュ値はスクリプト内の「空白」や「改行」一つでも変わる。CI/CDパイプラインで自動的にハッシュを計算し、設定ファイルを更新する仕組みを作るのが、プロのエンジニアの流儀だ。
---
4. 現場で「運用を止めない」ためのTips
「CSPを入れたらサイトが動かなくなった」という事態を避けるために、まずはContent-Security-Policy-Report-Onlyヘッダーから始めろ。
検証モード:ブロックせずに違反内容をレポートURLに飛ばす
add_header Content-Security-Policy-Report-Only "default-src 'self'; script-src 'nonce-random123'; report-uri /csp-violation-report;";
これを導入し、数日間ログを監視する。サードパーティ製ツールのタグ(Google Analyticsや広告タグ)がどこでブロックされているかを把握し、それらを適切にホワイトリストに追加する('strict-dynamic'を使うと、信頼されたスクリプトが動的にロードしたスクリプトまで許可できるため、運用負荷を下げられる)。
---
最後に:防御は「穴」を塞ぐだけではない
CSPは、いわば「Webアプリの境界防衛」だ。しかし、これだけで万全だと思うな。nonceが漏洩したり、サーバーサイドのテンプレートエンジンでXSS脆弱性が残っていれば、防御は突破される。
重要なのは、「多層防御」の精神だ。
1. 入力値の無害化(基本中の基本)
2. CSPによる実行制御(ここが今日のテーマ)
3. WAFによる不正アクセスの遮断(境界防御)
これらを組み合わせ、一つでも穴があればインシデントに直結するという緊張感を持って開発にあたってほしい。君たちが書く一行のコードが、ユーザーの情報を守る最後の砦になる。健闘を祈る。
コメント