セキュリティヘッダー(CSP・HSTS)
セキュリティヘッダーとは
セキュリティヘッダーは、サーバーが [[HTTP]] レスポンスに付けるブラウザへの指示書です。「ここ以外のスクリプトを実行するな」「このサイトを今後 HTTP で開くな」と宣言し、実際に取り締まるのはブラウザ側です。
Content-Security-Policy: default-src 'self'
Strict-Transport-Security: max-age=31536000
X-Content-Type-Options: nosniff
これは主対策の代わりになりません。[[XSS(クロスサイトスクリプティング)]] の対策はあくまで出力時のエスケープで、ヘッダーは漏れたときに被害を止める2枚目の壁です(多層防御)。
[[CORS]] とは方向が逆で、CORS は既定で禁止の別オリジンアクセスを緩め、セキュリティヘッダーは既定で許可の実行や埋め込みを絞ります。
CSP — スクリプトの出所を絞る
CSP(Content Security Policy)は、そのページが読み込んでよいリソースの出所を列挙するヘッダーです。
Content-Security-Policy: default-src 'self'; script-src 'self' https://cdn.example.com
攻撃者が <script src="https://evil.example/x.js"> を埋め込めても、出所が許可リストに無いためブラウザが読み込みを拒否します。
最初に驚くのは、CSPを入れると <script> タグに直接書いたコードと onclick="..." 属性が既定で動かなくなる点です。注入されるコードの大半がこの形だからです。正当なインラインは、リクエストごとに使い捨てる nonce(script-src 'nonce-r4nd0m2026' と <script nonce="r4nd0m2026"> を一致させる)で通します。固定値では攻撃者にコピーされ無意味です。
いきなり本番へ入れると広告や解析のタグが止まって画面が壊れます。まず観測だけのモードで様子を見ます。
Content-Security-Policy-Report-Only: default-src 'self'
-Report-Only 付きは何もブロックせず違反を報告するだけです。報告が落ち着いてから本番のヘッダー名へ切り替えます。
HSTS — HTTPへの降格を防ぐ
[[HTTPS・TLS]] を入れて HTTP を HTTPS へリダイレクトすれば安全、と思いがちですが穴があります。アドレス欄に example.com と打った最初の1回は平文で飛ぶため、そのリダイレクトを攻撃者が書き換えれば偽サイトへ誘導できてしまいます。
Strict-Transport-Security: max-age=31536000; includeSubDomains
受け取ったブラウザは以後 max-age 秒(この例は約1年)の間、そのドメインへのアクセスを送信前に HTTPS へ書き換えます。平文が流れる隙が消えます。
- 一度配ると取り消しづらい — 期限は利用者のブラウザに残り「一時的に HTTP で公開」ができません。
max-age=300(5分)から始めて延ばします includeSubDomainsは全サブドメインを巻き込む — HTTP のみの社内用サブドメインが到達不能になります
一度も訪れたことのない人にはヘッダー自体が届きません。これを消すのがプリロードで、最初から HSTS 済みとしてブラウザに同梱されるリストへ登録します。解除の反映が遅く、恒久的に HTTPS 専用にする覚悟がいります。
クリックジャッキングと frame-ancestors
クリックジャッキングは、攻撃者のページに本物のサイトを透明な iframe で重ね、「無料で受け取る」を押したつもりの利用者に裏の「退会する」を押させる攻撃です。押しているのは本物のサイトなので [[CSRF]] のトークン照合はすり抜けます。
| ヘッダー | 書き方 | 備考 |
|---|---|---|
| X-Frame-Options | DENY / SAMEORIGIN | 古い指定。細かい許可はできない |
| CSP frame-ancestors | 'none' / 'self' https://partner.example | 後継。許可元を個別に指定できる |
新規は frame-ancestors が基本ですが、古い環境向けに X-Frame-Options も併記するのが実務的です。
小さいが効く2つ
- X-Content-Type-Options: nosniff — ブラウザは
Content-Typeが怪しいと中身から型を推測します(MIMEスニッフィング)。アップロードされたファイルが HTML と解釈されれば XSS が成立します。nosniffは「宣言された型を信じろ」の指示です - Referrer-Policy — 遷移先には元の URL が
Refererとして送られます。https://example.com/reset?token=abc123から外部リンクを踏めばトークンごと渡りかねません。主要ブラウザの既定は現在strict-origin-when-cross-origin(外部へはオリジンまで)ですが、既定任せにせず明示しておくと確実です
初学者向けポイント
- 確認は開発者ツールの「Network」タブでレスポンスヘッダーを見るのが早いです
- 付与場所はアプリのコードでなく CDN やホスティングの設定のことが多く、本番には付いているのに検証環境では抜ける食い違いが起きがちです
- 影響の小さい
nosniff・Referrer-Policy・frame-ancestorsから入れ、CSP は Report-Only で観測してから本番化します - ヘッダーは既存の脆弱性を消しません。[[Webセキュリティ]] の基本を満たしたうえで重ねる保険です
関連技術とのつながり
- [[XSS(クロスサイトスクリプティング)]] — CSP が保険として効く相手。主対策のエスケープとは役割が違う
- [[CSRF]] — トークン照合をすり抜けるクリックジャッキングを frame-ancestors で防ぐ
- [[HTTPS・TLS]] — HSTS は「常に HTTPS」をブラウザに約束させる
- [[CORS]] — 既定の禁止を緩めるCORSに対し、ヘッダーは既定の許可を絞る
- [[Webセキュリティ]] — 多層防御の一層としての位置づけ
Q: セキュリティヘッダーによる防御を実際に実行しているのは誰?
- [ ] サーバーのファイアウォール
- [x] ヘッダーの指示を受け取ったブラウザ
- [ ] DNSサーバー
解説: サーバーは制限を宣言するだけで、スクリプトの読み込み拒否やiframe埋め込みの禁止を実行するのはブラウザです。
Q: CSPを本番へ安全に導入する進め方として本文が挙げたのはどれ?
- [ ] まず `default-src *` を設定してから徐々に狭める
- [x] Report-Onlyのヘッダーで違反を観測してから本番のヘッダー名に切り替える
- [ ] インラインスクリプトをすべて nonce 無しで残す
解説: Report-Only は何もブロックせず違反を報告するだけなので、壊れる箇所を洗い出してから本適用できます。
Q: HTTPからHTTPSへのリダイレクトだけでは不十分な理由として本文が挙げたのはどれ?
- [x] 最初の1回のアクセスが平文のHTTPで飛ぶため、途中で書き換えられる余地が残る
- [ ] リダイレクトはブラウザが無視する仕様のため
- [ ] HTTPSの証明書が自動で失効するため
解説: HSTSはブラウザがリクエストを送る前にHTTPSへ書き換えるため、この平文の1回目をなくせます。