3秒でわかる
ページが読み込んでよいスクリプトや画像の出どころを、ブラウザ側で制限する仕組み。XSS で差し込まれたコードの実行を止める最後の砦です。
30秒図解
もう少し詳しく
どういうものか
CSP は Content Security Policy の略で、そのページがどこから何を読み込んでよいかをブラウザに宣言するセキュリティ機能です。サーバーが Content-Security-Policy というレスポンスヘッダを返すと、ブラウザは許可されていない読み込みや実行をブロックします。
指定はディレクティブ単位です。script-src はスクリプト、img-src は画像、connect-src は fetch や WebSocket の接続先、frame-src は iframe の埋め込み先を制御します。どれにも当てはまらないものは default-src が受け持ちます。
なぜ必要か
XSS の対策は本来、入力値のエスケープで行います。ただし大きなアプリでエスケープ漏れをゼロにし続けるのは難しく、テンプレートの一箇所を直し忘れただけで攻撃が通ります。
CSP は、仮に攻撃者の タグが本文に混ざっても、許可した出どころ以外のスクリプトは動かさないという二段目の防御です。外部への情報送信も connect-src で塞げるため、盗んだクッキーを攻撃者のサーバーへ送る動きも止まります。
具体例
自分のドメインと決まった CDN だけを許可する例です。
Content-Security-Policy:
default-src 'self';
script-src 'self' https://cdn.example.com;
img-src 'self' data:;
connect-src 'self' https://api.example.com;
frame-ancestors 'none'Express で付けるなら次のようになります。
app.use((req, res, next) => {
res.setHeader(
"Content-Security-Policy",
"default-src 'self'; script-src 'self'; frame-ancestors 'none'"
);
next();
});つまずきやすいところ
いちばん多いのは、CSP を入れた途端に自分のサイトが壊れる事故です。インラインの や onclick 属性、style 属性は既定で禁止されるため、既存ページがそのまま動かなくなります。
対処として 'unsafe-inline' を付けると動きますが、それは CSP の効果をほぼ捨てる指定です。導入時は Content-Security-Policy-Report-Only で違反の報告だけ集め、実際に何が引っかかるかを見てから本適用に切り替えると安全です。
もうひとつ、frame-ancestors を書き忘れるとクリックジャッキング対策が抜けます。自サイトを iframe に入れさせない指定は別ディレクティブです。
覚え方
「入れないための対策がエスケープ、入っても動かさないのが CSP」と役割で分けて覚えます。