重要なHTTPセキュリティヘッダー:CSP、HSTS、X-Content-Type-Options、X-Frame-Options、Referrer-Policyの解説
HTTPレスポンスヘッダーがセキュリティに重要な理由
すべてのHTTPレスポンスはヘッダーを含んでいます——ボディの前に送信されるメタデータ行で、ブラウザがコンテンツをどのように処理するかを制御します。ほとんどのヘッダーは普通のもの(Content-Type、Cache-Control)ですが、特定のセキュリティヘッダーのセットは、ブラウザに制限を適用させ、攻撃のカテゴリ全体をブロックするよう指示します。これらは理論的な強化対策ではなく、OWASP Top 10やCVEデータベースに繰り返し登場する攻撃クラスに対する実践的な防御手段です。設定してもパフォーマンスへの影響はほとんどありません——各レスポンスに約200〜400バイトが追加されるだけ——securityheaders.comなどのツールは任意の公開URLに評価を付けるため、どのヘッダーが欠けているかをすぐに確認できます。
重要な点は、セキュリティヘッダーはサーバーではなくブラウザによって適用されることです。サーバーがポリシーを宣言し、ブラウザがそれを強制します。これは、高トラフィックサイトでのヘッダー設定の誤りが、すべての訪問者のブラウザに同時に影響することを意味します。逆に、適切なヘッダーを追加することで、単一のデプロイで何年分も積み重なったリスクを修正できます。ここで取り上げる5つのヘッダー——CSP、HSTS、X-Content-Type-Options、X-Frame-Options、Referrer-Policy——は、それぞれ攻撃者が悪用できる異なるブラウザの動作を標的にしています。
Content-Security-Policy:XSSファイアウォール
クロスサイトスクリプティング(XSS)はOWASP Top 10で最も一般的なWebの脆弱性です。HTMLに<script>タグを挿入できる攻撃者は、セッションCookieを盗んだり、ユーザーをリダイレクトしたり、フォームデータを窃取したりできます。Content-Security-Policy(CSP)はホワイトリストベースのヘッダーで、スクリプト、スタイル、画像、フォント、その他のリソースタイプに対してどのソースが信頼されているかをブラウザに伝えます。ディレクティブContent-Security-Policy: default-src 'self'は同じオリジンからのリソースのみを許可し、すべての外部スクリプトとインライン<script>ブロックをブロックします。script-srcディレクティブはJavaScriptに特化してデフォルトを上書きします。
CSP最大の落とし穴はunsafe-inlineキーワードです。script-src 'unsafe-inline'を追加するとインラインスクリプトが再有効化され、CSPのXSS保護が完全に無効になりますが、CSPを有効にした後にサイトが壊れた際に最もよく使われる回避策です。正しい代替方法はnonce(ノンス)です:各ページ読み込みで暗号学的にランダムな値を生成し、ヘッダーにscript-src 'nonce-r4nd0m'として、各正当な<script>タグにnonce="r4nd0m"として含めます。一致するnonceのない注入されたスクリプトは、インラインで現れても blocked されます。厳格なポリシーの準備ができていないチームには、Content-Security-Policy-Report-Onlyが何もブロックせずに違反レポートをコレクターエンドポイントに送信します——ポリシーを適用する前にその影響を測定する安全な方法です。
HTTP Strict Transport Security:HTTPSダウングレード攻撃の防止
SSLストリッピングは、2009年のBlack Hatで公開されたダウングレード攻撃です。経路上のアクティブな攻撃者がユーザーの最初のHTTPリクエスト(HTTPSへのリダイレクト前)を傍受し、暗号化された接続をユーザーに返しながら、プレーンHTTPとしてオリジンにプロキシします。ユーザーは一部のブラウザでは鍵のアイコンを見ますが、攻撃者とサーバー間の接続は暗号化されていない方——資格情報とセッションCookieがプレーンテキストで見えます。HTTP Strict Transport Security(HSTS)は、max-ageディレクティブで秒単位で定義された期間、ドメインのHTTP接続を完全に拒否するようブラウザに指示することでこのウィンドウを閉じます。
典型的な強力なHSTSヘッダーはStrict-Transport-Security: max-age=31536000; includeSubDomains; preloadです。31536000秒のmax-ageは1年——HSTSプリロードリストに含まれるために必要な最小値です。includeSubDomainsはポリシーをすべてのサブドメインに拡張します。preloadはhstspreload.orgのブラウザに同梱されたプリロードリストに含まれる意図を示します;一度リストに載ると、サイトを訪問してヘッダーを受信する前でも、ブラウザはHTTP接続を拒否します。重要な制約:HSTSは有効なHTTPSレスポンスで配信される必要があります。HTTP経由で送信されたヘッダーは無視されます。includeSubDomainsをデプロイする前にテストしてください——いずれかのサブドメインに有効なTLS証明書がない場合、max-age期間中ずっとユーザーはそこにアクセスできなくなります。
X-Content-Type-OptionsとX-Frame-Options:単一値で得られる2つのセキュリティ向上
MIMEスニッフィングは、一部のブラウザがレスポンスの最初の数バイトを検査してコンテンツタイプを推測し、Content-Typeヘッダーを上書きするレガシーな動作です。Internet Explorerは最も積極的なMIMEスニッファーで、最初のバイトがマークアップのように見えれば.jpgをHTMLとして実行していました。コンテンツをサイトにアップロードできる攻撃者(画像、ログファイル)は、ブラウザがスニッフィングした時にスクリプトとして実行されるペイロードを作成できます。X-Content-Type-Options: nosniffはすべての最新ブラウザに宣言されたContent-Typeを信頼し、スニッフィングを完全にスキップするよう指示します。有効な値はnosniffの一つだけで——すべてのレスポンスに設定しない理由はありません。
クリックジャッキングは、攻撃者が制御するページの<iframe>内にサイトを埋め込み、透明なボタンを重ねてユーザーが気づかずにサイトのUI要素をクリックさせます。X-Frame-Options: DENYは誰もページをフレーム化できないようにします;X-Frame-Options: SAMEORIGINは同じオリジンのページからのフレーム化のみを許可します。ALLOW-FROM uriバリアントは廃止されており、現在のブラウザではサポートされていません。現代の等価物はCSP frame-ancestorsディレクティブです(frame-ancestors 'none'はDENYに対応し、frame-ancestors 'self'はSAMEORIGINに対応)。また、複数の許可されたオリジンもサポートします。すべてのブラウザがすべてのコンテキストでframe-ancestorsを確認するわけではないため、X-Frame-OptionsとCSP frame-ancestorsディレクティブの両方を設定することで多層防御が提供されます。
Referrer-Policy:Refererヘッダーによる情報漏洩の制御
HTTP Refererヘッダー(RFCの歴史的な誤字のため'r'が1つだけ)は、ユーザーがナビゲートしてきたページの完全なURLを、そのページが読み込むすべての外部リソース——画像、スクリプト、スタイルシート、リンククリックの目的地——に送信します。ログインユーザーが/account/reset?token=eyJ...を訪問し、そのページがサードパーティの分析スクリプトを読み込む場合、スクリプトのリクエストはリセットトークンを含む完全なURLをRefererヘッダーに含んで送信します。referrer経由のトークン漏洩は十分に文書化された脆弱性クラスです;GitHubは2020年にreferrerベースのトークン漏洩を修正しました。
Referrer-Policyヘッダーは以前のMeta referrerメカニズムとRefererヘッダー抑制の回避策を置き換えます。ポリシーstrict-origin-when-cross-originは推奨されるデフォルトです:同一オリジンのリクエストには完全なURL(内部分析に役立つ)を送信しますが、クロスオリジンリクエストには裸のオリジン(https://example.com)のみを送信し、HTTPSからHTTPへのナビゲーション時には何も送信しません。no-referrerは何も送信せず、プライバシーを最大化しますが、referrerに依存する分析を壊します。unsafe-urlは常にパスとクエリ文字列を含む完全なURLを送信します——2020年以前のブラウザのデフォルトで、歴史的なreferrer漏洩のほとんどの原因です。Chrome、Firefox、Safariはすべて2020〜2021年にポリシーヘッダーがない場合のデフォルトとしてstrict-origin-when-cross-originを採用しましたが、明示的に設定することですべてのブラウザバージョンで一貫した動作が保証されます。
あらゆるサイトのための実践的なセキュリティヘッダーベースライン
静的サイトやシンプルなWebアプリケーションの場合、4つのヘッダーが最小限の設定複雑さで最も一般的な攻撃ベクトルをカバーします:X-Content-Type-Options: nosniff(常に、1行);X-Frame-Options: SAMEORIGIN(サイトを正当に埋め込む必要がない限り);Referrer-Policy: strict-origin-when-cross-origin;そしてStrict-Transport-Security: max-age=31536000; includeSubDomains(すべてのサブドメインがHTTPSであることを確認したら)。CSPはより多くの計画が必要です——Content-Security-Policy-Report-Onlyとレポートエンドポイントから始め、数日間違反を収集し、実際に使用しているものだけを許可するポリシーを作成してください。
ヘッダー設定の場所はスタックによって異なります:NginxはサーバーブロックでAdd_headerディレクティブを使用;Apacheはhttpd.confまたは.htaccessでHeader always setを使用;Vercel、Netlify、Cloudflare Pagesにはそれぞれヘッダー設定ファイルまたはダッシュボード設定があります。ほとんどのCDNはエッジでのヘッダーインジェクションを許可しており、CDNはキャッシュされたレスポンスにも配信するため、アプリケーションレベルのヘッダーよりも好ましいです。TeaFun HTTP Headers Checkerツールは任意の公開URLを取得し、存在するセキュリティヘッダー、その値、各ヘッダーが何をするかのクイックリファレンス説明を表示します——ヘッダーの変更をデプロイする前後にサイトを監査するために使用してください。