セキュリティヘッダ — 最後の1枚を、ブラウザに張らせる
XSS は塞いだつもりだった。広告タグの1つが改ざんされ、そこから入力欄の値が外部へ送られた。自前のコードは1行も侵されていない。
この記事の進み方
What — セキュリティヘッダは「ブラウザへの指示」
セキュリティヘッダは、サーバーがブラウザに対して制約を宣言する仕組みです。アプリ側で何かを検査するのではなく、ブラウザに禁止させます。
どこから読み込んでよいか、どこへ送ってよいかを制限する。
Content-Security-Policy: default-src 'self'; script-src 'self' cdn.example.com; connect-src 'self' api.example.com; frame-ancestors 'none'
- 防ぐもの
- XSS の実行・情報の持ち出し
- 導入の手間
- 大きい
- 壊れやすさ
- 間違えると画面が壊れる
- –connect-src が効けば、冒頭の事故で外部への送信を止められた。
- –report-only モードで、壊さずに違反だけ集められる。
- –インラインの script を禁じるため、既存サイトへの後付けは手間がかかる。
どれも「最後の1枚」であって、これだけで守るものではない。本体の対策があったうえで、間違えたときの被害を小さくする。
- CSP
- Content Security Policy。読み込み元と送信先をブラウザに制限させる仕組み。
- report-only
- 違反を報告するだけで、実際にはブロックしないモード。導入の下見に使う。
- nonce
- リクエストごとに変わる使い捨ての値。これを持つ script だけを許可する。
- HSTS
- HTTP Strict Transport Security。HTTPS での接続を強制する指示。
- クリックジャッキング
- 透明な iframe に重ねて、利用者に意図しない操作をさせる攻撃。
CSP が防ぐのは「実行」と「持ち出し」の両方
CSP は2方向を制限できます。
script-src— どこから読み込んだスクリプトを実行してよいかconnect-src— どこへ通信してよいか
冒頭の事故では、読み込みは許可していたドメインから攻撃が来ました。script-src では止まりません。
しかし connect-src が効いていれば、外部への送信が止まります。盗んだ値を持ち出せなければ、被害はほぼ出ません。
「入られる」と「持ち出される」は別の段階で、それぞれに守りを置けます。
Why — アプリの対策だけでは、届かない範囲がある
自分が書いていないコードは、レビューできない
<script src="..."> で読み込む外部のコードは、中身がいつでも差し替わります。
レビューした時点のコードと、利用者のブラウザで実行されるコードが同じである保証はありません(security/supply-chain)。
広告、解析、チャットのウィジェット、A/B テスト。これらは全部、こちらのページで、こちらの権限で動きます。
エスケープは「入力の対策」でしかない
XSS 対策のエスケープは、利用者の入力が HTML として解釈されるのを防ぐものです。
冒頭の事故には利用者の入力が関与していません。攻撃者は正規の <script> タグを使いました。 エスケープでは触れられない経路です。
- 1利用者の入力
- 2自社のコード
- 3外部のスクリプト
- 4外部への送信
アプリ側の対策は左側にしか置けない。右側(読み込んだコードが何をするか)は、ブラウザに制限させるしかない。
「効いているか」が見えにくい
セキュリティヘッダの厄介なところは、設定しても何も起きないことです。
正しく設定されていれば何も起きず、設定されていなくても何も起きません。攻撃が来るまで差が出ない。
だから「設定したつもり」が放置されます。タイプミスで無効なヘッダになっていても、誰も気づきません。
演習 — まず自分で判断する
冒頭の事故で、被害を防げた可能性が最も高い設定はどれですか?
HSTS を `max-age=31536000; includeSubDomains` で設定しました。注意すべきことは何ですか?
読み終わりましたか?
読了にすると、これを前提とする記事がロードマップで開放されます。