CSPは「お守り」ではない。XSSを無力化する「防弾チョッキ」だ
いいか、現場のエンジニア諸君。セキュリティ対策の多くは「性善説」に基づいたザルになりがちだが、Content-Security-Policy (CSP) だけは別格だ。
「入力値をエスケープしてるからXSSは大丈夫」なんて言葉、もう聞きたくない。DOM-based XSSや、ライブラリの脆弱性を突いた意図しないスクリプト実行は、コードの修正だけで防ぐのはほぼ不可能だ。CSPは、たとえ脆弱性が残っていても「攻撃者のコードをブラウザ上で実行させない」という、最後の砦になる。
今日は、教科書的な説明はすっ飛ばして、「明日から使える、本当に破られないCSPの構成」について話す。
—
攻撃者が狙う「盲点」:なぜ従来のCSPでは不十分なのか
多くの開発者が導入する script-src 'self' という設定。これ、実はかなり危うい。なぜなら、CDN経由で読み込んでいる古いJSライブラリや、JSONPエンドポイント、あるいはアップロードされた画像の中に偽装されたスクリプトが混入した場合、いとも簡単に突破されるからだ。
攻撃者は、お前たちが許可したドメイン内に「実行可能なスクリプト」を配置する隙を常に狙っている。だからこそ、Nonce(ナンス:使い捨てトークン)による厳格なホワイトリスト運用が必須なんだ。
—
実装編:Nonceを用いた「鉄壁」の構成
Nonceを使うと、サーバー側で生成したランダムな文字列と一致する script タグしか実行を許可しない。攻撃者が外部から悪意あるスクリプトを注入しても、正しいNonceを推測できない限り、ブラウザは即座に実行を拒否する。
1. サーバーサイドでのNonce生成(PHPの例)
まずはリクエストごとにユニークなNonceを発行する。
2. Nginxでのレスポンスヘッダー設定
フロントエンド側で動的に設定できない場合、あるいはインフラ層で強固に守りたい場合は、Nginx側でベースラインを定義しておく。
Nginx設定ファイル
add_header Content-Security-Policy “default-src ‘self’; script-src ‘self’; object-src ‘none’; frame-ancestors ‘none’; sandbox allow-forms allow-scripts; report-uri /csp-report;”;
object-src 'none'は、Flash等のプラグインによる脆弱性を排除する必須設定だ。frame-ancestors 'none'は、クリックジャッキング攻撃を防ぐための現代の標準だ。
—
運用で死なないための「レポート機能」の活用
CSPをいきなり厳格に適用すると、既存の正当な機能が動かなくなり、現場が火の車になる。そこでまずは Content-Security-Policy-Report-Only ヘッダーを使って、試験運用から始めるのが鉄則だ。
Content-Security-Policy-Report-Only: default-src ‘self’; script-src ‘nonce-random123’; report-uri /api/csp-report;
この設定なら、ポリシー違反があってもブロックされず、ログだけがサーバーに飛んでくる。このレポートを収集し、「本当に許可すべきドメインはどこか?」を精査するんだ。Sentryのようなエラー監視ツールを使うと、どのコードがブロックされたのか一目瞭然になる。
—
最後に:セキュリティは「設定して終わり」じゃない
いいか、CSPは銀の弾丸ではない。'strict-dynamic' を使えば運用は楽になるが、依存関係の把握を怠れば、結局は脆弱なライブラリを許可することになる。
1. インラインスクリプトを徹底的に排除する(jsファイルに分離しろ)。
2. Nonceを使い、動的なスクリプト生成を厳格に管理する。
3. レポートを見て、拒否されている通信に異常がないか毎日確認する。
セキュリティは「泥臭い作業の積み重ね」だ。華やかなハッキングのニュースに一喜一憂する前に、今すぐ自分のWebアプリのヘッダーを見てくれ。そこには、君の守りの甘さが如実に現れているはずだ。
もし設定で行き詰まったら、いつでも質問してくれ。現場の最前線で、一緒に戦おう。
コメント