ウェブサイトの安全性の大半はコードの中にありますが、そのコードが届いたあとブラウザが何をしてよいかを決めるのは、少数のHTTPレスポンスヘッダーです。注入された<script>が実行されるか、ブラウザが実行を拒むか。最初の訪問が暗号化されないHTTPで行われるか、アドレスバーを離れる前にHTTPSへ引き上げられるか。その違いを生むのがこれらのヘッダーです。
しかも導入費用が驚くほど安い。ヘッダーはコードではなく設定です。ほとんどはウェブサーバー、CDN、フレームワークの一行で済み、すべてのレスポンスに一度で適用されます。本ガイドでは各ヘッダーの役割、見合うもの、自分のサイトを壊さずにContent-Security-Policyを導入する手順、そして今あなたが何を送っているかを確認する方法を扱います。
セキュリティヘッダーが重要な理由
ヘッダーはブラウザへの指示であり、利用者の端末上でそれを強制できるのはブラウザだけです。だからヘッダーは、手前の段階で既に何かが失敗していても機能し続ける第二の防御線になります。
- すでに作ってしまった不備を封じ込める。 Content-Security-PolicyはXSSの穴を直しませんが、注入されたスクリプトの実行や、そこから外部への送信を止められます。
- 攻撃の種類をまとめて封じる。クリックジャッキング、MIMEの誤認、HTTPへの後退はあなたのコードの不具合ではなく、明示的に拒否しなければ有効なままのブラウザの挙動です。
- 静かな情報漏えいを止める。リファラーポリシーがなければ、非公開ページの完全なURLが、画像を読み込むすべての第三者へ渡ります。
- 監査で最初に見られる。欠けたヘッダーは自動スキャン、セキュリティ質問票、ベンダー審査のすべてに現れます。もっとも安価に指摘できる項目だからです。
やっかいなのは「ほぼ正しい」状態にしやすい点です。unsafe-inlineを含むCSPはほとんど何も遮断せず、有効期間が1日のHSTSヘッダーはほぼ誰も守りません。価値は細部にあります。
もっとも重要なヘッダー
有用な仕事のほぼすべてを6つのヘッダーが担います。残りは旧式か、対象が狭すぎるか、後継に置き換わったものです。
| ヘッダー | 防げること | 手間 |
|---|---|---|
Content-Security-Policy | XSS、データ持ち出し、想定外の外部コード | 大 |
Strict-Transport-Security | HTTPへの後退、初回リクエストでのCookie窃取 | 小 |
X-Content-Type-Options | アップロードされたファイルがスクリプトとして実行されること | ごく小 |
Referrer-Policy | 完全なURLが第三者へ漏れること | ごく小 |
Permissions-Policy | カメラ、マイク、位置情報への利用 | 小 |
Cross-Origin-Opener-Policy | 開いたページを経由したウィンドウ間攻撃 | 小 |
下から始めてください。安価な4つは各一行で、まず何も壊しません。今日すぐ出す価値があります。計画が必要なのはCSPです。
Content-Security-Policy
CSPは、各種リソースがどこから来てよいかをブラウザに伝える許可リストです。相応に厳格なポリシーは次のようになります。
default-src 'self'; script-src 'self' 'nonce-r4nd0m'; style-src 'self'; img-src 'self' data:; connect-src 'self'; frame-ancestors 'none'; base-uri 'self'; object-src 'none'
ディレクティブごとに読んでいきます。default-src 'self'は既定の受け皿で、明示されていないものは自分のオリジンからしか読み込めません。もっとも重要なのはscript-srcです。攻撃者が実行したいのはスクリプトだからです。frame-ancestors 'none'は、他サイトがあなたのページをiframeに入れることを禁じ、X-Frame-Optionsの現代的な代替です。object-src 'none'とbase-uri 'self'は、閉じる費用がゼロの古い抜け道を二つ塞ぎます。
ポリシーが本物かどうかは二つの値で決まります。script-srcの'unsafe-inline'は、攻撃者が注入したものも含めてあらゆるインライン<script>を許します。それはまさにCSPが止めるために存在するものです。'unsafe-eval'はeval()とその仲間を許します。どちらかを含むポリシーもスクリプト以外のディレクティブには役立ちますが、XSSからは守ってくれません。
出口はnonceかハッシュです。nonceはレスポンスごとに生成する乱数値で、ヘッダーと正規の<script nonce="...">の両方に付けます。ブラウザはその値を持つスクリプトだけを実行し、注入されたスクリプトはその値を推測できません。内容が変わらないスクリプトにはハッシュが同じ働きをします。フレームワークがインラインスクリプトを出力しているなら、ほぼ確実にnonce対応が既にあります。有効化は多くの場合、設定を一つ切り替えるだけです。
Strict-Transport-Security
HSTSは、指定した期間、あなたのドメインに対して暗号化されないHTTPを拒むようブラウザへ伝えます。これはリダイレクトだけでは開いたままになる隙、つまりリダイレクトが届く前の最初のリクエストが平文で流れ、傍受され得るという隙を塞ぎます。
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
max-ageは秒数です。1年が通例の目標で、数か月を下回ると効果は乏しくなります。includeSubDomainsは規則をすべてのサブドメインへ広げます。これが効果を生む要であり、同時に最初に確認すべき点でもあります。まだHTTPで配信されているサブドメインは動かなくなります。preloadは、ブラウザに同梱される一覧への収録を申請するもので、初回の訪問まで保護されます。
HSTSは元に戻しにくいものとして扱ってください。1年のポリシーを見たブラウザは、その後あなたが何を送っても1年間それを適用します。短いmax-ageで導入し、何も壊れていないことを確かめてから引き上げます。そしてHSTSは背後の証明書が有効でなければ意味がないので、長い期間を約束する前にSSL証明書チェッカーで証明書と期限を確認してください。
小さなヘッダー
次の4つは各一行で、議論になることもまずありません。
X-Content-Type-Options: nosniff— レスポンスの種類を中身から推測することをブラウザに禁じます。これがないと、.txtとしてアップロードされたファイルが、スクリプトに見えるとブラウザが判断すれば実行され得ます。Referrer-Policy: strict-origin-when-cross-origin— 自分のオリジンへは完全なURLを、他のHTTPSサイトへはオリジンだけを送り、HTTPへ落ちるときは何も送りません。これが現代の既定で、非公開のパスやクエリが第三者へ漏れるのを防ぎます。Permissions-Policy: camera=(), microphone=(), geolocation=()— サイトが使っていない端末機能を、自分のページと埋め込んだものの両方で無効にします。Cross-Origin-Opener-Policy: same-origin— 自分が開いたページ、あるいは自分を開いたページとのウィンドウ関係を断ちます。ウィンドウ間攻撃の一群を防ぎ、ブラウザの一部の分離機能の前提にもなります。
古いブラウザ向けにはX-Frame-Options: DENYもなお一覧に残りますが、CSPのframe-ancestorsがそれを置き換え、表現力も上です。両方送って構いません。衝突しません。
CSPを安全に導入する手順
当て推量で書いたCSPは、自分のサイトに必要な何かを遮断します。「まず報告」の道筋を踏めば、それは完全に避けられます。
- 安価な4つを今日出す。
nosniff、リファラーポリシー、権限ポリシー、そして短いmax-ageのHSTS。いずれも通常、動いているサイトを壊しません。 Content-Security-Policy-Report-Onlyを加える。書式は同じで、ブラウザは違反を遮断せず報告するだけです。サイトの挙動は以前とまったく同じです。- 1〜2週間、報告を集める。忘れていた外部スクリプト、フォント、解析のエンドポイント、インラインのハンドラがすべて挙がります。皆が飛ばす工程であり、残りを苦痛なくする工程です。
- 報告が静かになるまで締める。本当に必要な配信元を加え、不要だと分かったものを外します。報告ひとつを黙らせるためにディレクティブを
*へ広げたい誘惑には抗ってください。 - 適用モードへ切り替える。ヘッダー名を
Content-Security-Policyに変えます。より厳しいポリシーを後で試すために、報告専用版も併置しておきます。 unsafe-inlineは最後に外す。ここが本番です。インラインスクリプトをファイルへ移すか、nonceを与えます。それ以前はすべて準備で、XSSを止めるのはこの一歩です。
実務上の注意として、ヘッダーはレスポンス単位なので、ページごとではなく縁で設定してください。CDN、リバースプロキシ、フレームワークのミドルウェアです。一か所で設定することは、エラーページやリダイレクトにもヘッダーが付くと確信できる唯一の方法でもあり、まさにそこが抜けがちな場所です。
送るのをやめてよいヘッダー
一部の助言は古くなりました。死んだヘッダーを送ることは無害ではありません。バイト数が増え、終わっていない監査を終わったように見せます。
X-XSS-Protection— 制御していたブラウザのフィルタ自体が消えました。現行のどのブラウザでも何もせず、古いmode=blockはかつて独自の問題を招きました。外してCSPに頼ってください。Public-Key-Pins— HPKPはブラウザから削除されました。自分のドメインから自分を締め出しやすく、それが放棄された理由です。Expect-CT— 証明書の透明性はすでに既定で強制されるため、このヘッダーは不要です。Feature-Policy—Permissions-Policyへ改称され、書式も異なります。新しい方を送り、古い方は非常に古い環境を支える必要がある場合だけ残してください。
逆に外す価値があるのは、自分から差し出している情報です。フレームワークの正確なバージョンを名乗るServerやX-Powered-Byは、どの攻撃手段を先に試すべきかを攻撃者に教えます。脆弱性ではありませんが、公開する理由もありません。
自分のヘッダーを確認する方法
ヘッダーは公開情報なので、どのサイトの設定も数秒で確認できます。
curl -I https://example.com。HEADリクエストはページを取得せずにすべてのレスポンスヘッダーを表示します。リダイレクトではなく最終URLを見るには-Lを加えます。- ブラウザのネットワークパネル。開発者ツールを開き、再読み込みし、最上位のドキュメントを選んでレスポンスヘッダーの欄を読みます。コンソールにはCSP違反と遮断したディレクティブも記録され、ポリシーのデバッグはこれが最速です。
- 結果を評価してくれるチェッカー。セキュリティヘッダーチェッカーはURLを取得し、どのヘッダーがあり、どれが欠け、どこで見かけより弱いかを示します。
トップページだけで済ませないでください。ヘッダーは本体アプリには設定されていても、静的ファイルのホスト、エラーページ、APIのサブドメイン、wwwから本来のドメインへ送るリダイレクトには付いていないことがよくあります。ついでに、リダイレクトの連鎖そのものが正しいかも確かめましょう。HTTPステータスコードのリファレンスが、恒久的な移動にどの番号がふさわしいかを扱っています。
構文を覚えずにポリシーを書くには、弱める設定も指摘してくれるCSP ジェネレーターから始めましょう。
よくある間違い
script-srcにunsafe-inlineを含むCSP。自動チェックはほぼ通りますが、XSSはほとんど止まりません。nonceかハッシュを使ってください。- 報告ひとつを消すためにディレクティブを
*へ広げる。代わりに該当のオリジンだけを加えます。*はそのディレクティブを丸ごと手放すのと同じです。 - 初日から長い
max-ageのHSTS。公開した期間のあいだ、事実上取り消せません。短く始めてください。 - サブドメインを調べる前の
includeSubDomains。まだHTTPのもの、社内向けの道具も含めて読み込めなくなります。 - アプリ本体にはあるが縁にはないヘッダー。エラーページ、リダイレクト、ファイルのホストが無防備になり、攻撃者が狙うのはまさにそれらのレスポンスです。
- 同じヘッダーを二重に設定する。 CSPが二つあると統合ではなく積集合になるため、どちらより厳しくなり、たいてい何かが壊れます。
X-Frame-Optionsの値が矛盾していると、まるごと無視されることもあります。 X-XSS-Protectionを送り続けて対応済みと考える。死んだヘッダーは報告書の上で誤った安心を与えます。- 報告専用モードから先へ進まない。対応しない報告は誰も守りません。
自分のヘッダーを確認する
セキュリティヘッダーは、ウェブで得られるもっとも効率のよい防御です。4つは各一行、5つ目のCSPは、何かを強制する前に自分の違反報告を聞くことがほとんどすべてです。まず今サイトが何を送っているかを見て、安価なヘッダーをすぐに足し、それから報告専用のCSPを置いて、ポリシーに何を書くべきかをそれ自身に教えてもらってください。
関連ツールとガイド:
- セキュリティヘッダーチェッカー — URLが送るヘッダーと欠けているヘッダーを確認。
- SSL証明書チェッカー — HSTSポリシーの背後にある証明書を検証。
- HTTPステータスコード — HTTPSリダイレクトが正しい番号を返しているか確認。
- DNSルックアップ完全ガイド — ポリシー内の各ホスト名の背後にあるレコード。
よくある質問
セキュリティヘッダーはどれから追加すべきですか?⌄
まず、ほぼ何も壊さない4つから始めてください。X-Content-Type-Options: nosniff、Referrer-Policy: strict-origin-when-cross-origin、使っていない端末機能を無効にするPermissions-Policy、そして後で引き上げる短いmax-ageのStrict-Transport-Securityです。そのうえで報告専用モードのContent-Security-Policyを追加します。適用の前に観察が必要なのはこれだけです。
unsafe-inlineを含むCSPでも役に立ちますか?⌄
CSPが最も得意とする攻撃を除けば、役に立ちます。img-src、connect-src、frame-ancestors、object-srcなどのディレクティブは、データの行き先や誰があなたを枠に入れられるかを依然として縛ります。しかしscript-srcの'unsafe-inline'は注入されたインラインスクリプトの実行を許すため、XSSからは守れていません。レスポンスごとのnonceかスクリプトのハッシュに置き換えてください。
Strict-Transport-Securityは危険ですか?⌄
危険なのは主に取り消しにくいからです。max-age=31536000を見たブラウザは、その後あなたが何を送っても1年間、あなたのドメインへの暗号化されないHTTPを拒みます。まず短いmax-ageで導入し、includeSubDomainsを足す前にすべてのサブドメインがHTTPSで到達できることを確かめ、そのうえで期間を延ばしpreloadを検討してください。
X-Frame-OptionsとX-XSS-Protectionの代わりは何ですか?⌄
Content-Security-Policyのframe-ancestorsディレクティブがX-Frame-Optionsを置き換え、表現力も上です。両方送っても無害で、古いブラウザには今も役立ちます。X-XSS-Protectionには代わりがありません。制御していたブラウザのフィルタが削除されたからです。XSSを止めるのは今はCSPであり、古いヘッダーは単に外してかまいません。
セキュリティヘッダーはどこで設定すべきですか?⌄
縁で、一か所にまとめます。CDN、リバースプロキシ、フレームワークのミドルウェアです。ページごとに設定すると、エラーページ、リダイレクト、ファイルのホスト、APIのレスポンスが未対応のまま残ります。それらはまさに監査で見落とされ、攻撃で狙われるレスポンスです。
自分のサイトが送っているヘッダーはどう確認しますか?⌄
curl -I https://yourdomain.com を実行し、リダイレクトではなく最終的な宛先を見るために -L を加えます。あるいはブラウザのネットワークパネルを開き、最上位ドキュメントのレスポンスヘッダーを読みます。セキュリティヘッダーチェッカーはこれをブラウザから行い、欠けているヘッダーと、存在はするが見かけより弱いヘッダーの両方を示します。
Web 開発とセキュリティに 15 年以上携わってきた実務経験を持つ。実践的な Web セキュリティ、つまりサイトを安全・確実に保ち、トラブルを素早く解決できるようにすることに注力。BuildToolForge は、こうしたチェックを無料で、プライベートに、わかりやすく行えるようにするためのサイトです。ツールは可能な限りブラウザ内で動作し、ツールに入力した内容は保存されません。
ガイドの書き方: すべてのガイドは実務経験をもとに執筆し、公式の標準と照らし合わせ、実際のドメインで自社ツールを使って検証しています。標準やツールが変わればガイドも更新します。誤りを見つけたら contact@buildtoolforge.com までお知らせください。