【実務・中級編】Content-Security-Policy (CSP) によるXSSの多層防御 – アプリケーションセキュリティ & 安全な開発防御ガイド

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アプリのヘッダーを見てくれ。そこには、君の守りの甘さが如実に現れているはずだ。

もし設定で行き詰まったら、いつでも質問してくれ。現場の最前線で、一緒に戦おう。

コメント

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