「うちのサイトはHTTPS対応済みだから大丈夫」——そう思っているエンジニアほど、実は見落としている落とし穴があります。
HTTPSはデータの暗号化と通信経路の保護には有効です。しかし、ブラウザ上でのスクリプト実行制御や、フレーム内での悪用防止といった攻撃には対応できません。この部分を担うのが、HTTPレスポンスヘッダーによるセキュリティ制御です。
この記事では、Webサーバーに設定すべきセキュリティヘッダー6種の仕組みと、Nginx・Apacheでの具体的な設定手順を現場目線で解説します。無料ツールによる設定確認方法もあわせてご紹介します。
セキュリティヘッダーとは?なぜHTTPSだけでは足りないのか
HTTPレスポンスヘッダーとは、WebサーバーがブラウザにHTMLを返す際に付加するメタ情報です。通常のコンテンツとは別のレイヤーで、ブラウザの動作を制御します。
たとえば、「このページ上では外部ドメインのスクリプトを実行してはならない」という命令をブラウザに伝えられます。HTTPSはあくまで「通信の保護」であり、「ページ上で何を実行するか」という制御はカバーしていません。
セキュリティヘッダーが未設定のサイトが攻撃者に狙われる主な理由を整理します。
・XSS(クロスサイトスクリプティング)の増幅: コードインジェクションが成立した場合の被害が広がる
・クリックジャッキングの踏み台: 悪意あるサイトのiframeに自社ページを埋め込まれる
・MIMEスニッフィング攻撃: ブラウザがファイルタイプを誤認して悪意のあるコードを実行する
・HTTPSダウングレード攻撃: HSTS未設定では初回アクセスを平文にリダイレクトできる
設定コストは低く、効果は高い——これがセキュリティヘッダーの最大の特徴です。
攻撃者はどうセキュリティヘッダーの不備を突くのか
攻撃者がターゲットサイトを調査するとき、最初にやることの一つがセキュリティヘッダーの確認です。ブラウザのデベロッパーツール(F12)を開き、Networkタブでレスポンスヘッダーを見るだけで、どのヘッダーが設定されているかが一目でわかります。
防御のために主な攻撃シナリオを理解しておきましょう。
【シナリオ1】CSP未設定 → XSS攻撃の被害が拡大しやすくなる
CSP(Content-Security-Policy)が未設定の場合、攻撃者がデータベースやフォームの脆弱性を利用して悪意あるスクリプトを挿入すると、ブラウザはそのスクリプトを無条件に実行します。CSPが設定されていれば、許可されていない外部ドメインからのスクリプト読み込みをブラウザ側でブロックできます。
【シナリオ2】X-Frame-Options未設定 → クリックジャッキング
X-Frame-Optionsが設定されていないと、攻撃者は自分のサイトのiframeに被害者サイトを読み込み、透明なレイヤーを重ねて表示できます。ユーザーが見えないボタンをクリックしたつもりで、実際には被害者サイト上の操作が実行されるというのがクリックジャッキングの手口です。ログイン状態のまま操作させることで、意図しない送金・設定変更などが行われる可能性があります。
【シナリオ3】HSTS未設定 → SSL Stripping攻撃
HSTSが設定されていない場合、ユーザーが最初に「http://」でアクセスした瞬間、同一ネットワーク内の攻撃者はその通信を傍受してHTTPのままサイトを返すことができます(SSL Stripping攻撃)。ユーザーはHTTPSに接続したと思っていても、実際は平文通信になっているケースがあります。
主要セキュリティヘッダー6種と設定値
1. HSTS(HTTP Strict Transport Security)
指定した期間、ブラウザに「このドメインは必ずHTTPSで接続する」と記憶させるヘッダーです。初回訪問後は、たとえ「http://」でアクセスしてもブラウザ側でHTTPSにリダイレクトします。
# 推奨設定(max-age は1年 = 31536000秒) Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
preloadディレクティブは、Googleが管理するHSTSプリロードリストへの登録申請を意味します。一度登録すると削除に数ヶ月かかるため、まずpreloadなしで試験運用してから追加するのが安全な進め方です。
2. Content-Security-Policy(CSP)
ページ上で読み込んでよいリソースのソース(スクリプト・スタイル・画像など)をホワイトリスト形式で指定するヘッダーです。XSS対策として最も強力なHTTPヘッダーの一つですが、設定の複雑さゆえに未設定のまま運用されているサイトも少なくありません。
# 段階的な導入例(まずはReport-Onlyモードで開始) Content-Security-Policy-Report-Only: default-src 'self'; script-src 'self' https://www.googletagmanager.com; report-uri /csp-report # 違反レポートを確認後、Enforceモードに移行 Content-Security-Policy: default-src 'self'; script-src 'self' https://www.googletagmanager.com; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:;
いきなりCSPをEnforceモードで設定すると、GTMやGA4などの外部スクリプトが動作しなくなることがあります。まずContent-Security-Policy-Report-Onlyヘッダーで違反レポートを収集してから本番適用するのが確実です。
3. X-Frame-Options
自サイトを他のサイトのiframeやframeに埋め込むことを制御するヘッダーです。クリックジャッキング対策の定番として長く使われてきました。
# DENY: いかなるiframeにも埋め込みを許可しない(推奨) X-Frame-Options: DENY # SAMEORIGIN: 同一オリジンのiframeのみ許可 X-Frame-Options: SAMEORIGIN
モダンブラウザでは、CSPのframe-ancestorsディレクティブで同様の制御が可能です。ただし、旧ブラウザへの後方互換性のために、X-Frame-Optionsも併用するのが確実です。
4. X-Content-Type-Options
ブラウザによるMIMEタイプのスニッフィング(推測)を防ぐヘッダーです。設定値はnosniffのみで、設定コストが最も低い割に確実な効果があります。
# ブラウザがMIMEタイプを勝手に推測するのを防ぐ X-Content-Type-Options: nosniff
攻撃者がJavaScriptとして実行させたいファイルを「text/plain」として配置し、ブラウザがスクリプトとして解釈するパターンを防ぎます。
5. Referrer-Policy
外部リンクをクリックしたときに送信するリファラー情報(どのページから来たか)の範囲を制御するヘッダーです。URLにセッションIDやユーザー情報が含まれていた場合の情報漏洩を防ぎます。
# HTTPS→HTTPへの遷移時にリファラーを送らない(推奨) Referrer-Policy: strict-origin-when-cross-origin # より厳格な設定(オリジンのみ送信) Referrer-Policy: strict-origin
6. Permissions-Policy(旧Feature-Policy)
カメラ・マイク・位置情報など、ブラウザのAPIへのアクセスを制限するヘッダーです。悪意あるサードパーティスクリプトが不要な機能にアクセスするのを防ぎます。
# カメラ・マイク・位置情報を無効化する例 Permissions-Policy: camera=(), microphone=(), geolocation=()
Nginxで全ヘッダーを一括設定する実践手順
Nginxではadd_headerディレクティブをサーバーブロックまたはlocationブロックに追加します。管理しやすいよう、セキュリティヘッダーをまとめた専用の設定ファイルを作成してincludeする方法を推奨します。
# /etc/nginx/conf.d/security-headers.conf として作成 add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always; add_header X-Frame-Options "DENY" always; add_header X-Content-Type-Options "nosniff" always; add_header Referrer-Policy "strict-origin-when-cross-origin" always; add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always; # CSPは環境に合わせて調整すること(以下はサンプル) # add_header Content-Security-Policy "default-src 'self'; script-src 'self' https://www.googletagmanager.com; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:;" always;
# メインのnginx.conf(または各サーバーブロック)でinclude include /etc/nginx/conf.d/security-headers.conf; # 設定の構文チェック nginx -t # 設定のリロード systemctl reload nginx
alwaysパラメータを付けないと、エラーページ(4xx・5xx)ではヘッダーが付与されません。本番環境では必ずalwaysを付けましょう。
Apacheでの設定方法
Apacheではmod_headersモジュールが必要です。有効になっていることを確認してから設定します。
# mod_headersが有効か確認 apache2ctl -M | grep headers # 有効化(Ubuntu/Debian系) a2enmod headers systemctl restart apache2
# /etc/apache2/conf-available/security-headers.conf Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains" Header always set X-Frame-Options "DENY" Header always set X-Content-Type-Options "nosniff" Header always set Referrer-Policy "strict-origin-when-cross-origin" Header always set Permissions-Policy "camera=(), microphone=(), geolocation=()"
# 設定ファイルを有効化して反映 a2enconf security-headers apache2ctl configtest systemctl reload apache2
セキュリティヘッダーの確認・テスト方法
設定後は必ず動作確認を行います。現場でよく使われる確認方法を3つ紹介します。
1. curlコマンドで即確認
# レスポンスヘッダーのみ表示(-I はHEADリクエスト) curl -I https://your-domain.com # セキュリティヘッダーだけ抽出して確認 curl -sI https://your-domain.com | grep -i -E "strict-transport|x-frame|x-content|referrer|permissions|content-security"
2. SecurityHeaders.comでスコア確認
「securityheaders.com」でURLを入力すると、設定されているセキュリティヘッダーをA+~Fのグレードで評価してくれます。設定前後でスコアを比較するのに便利で、業界標準との乖離も一目でわかります。HTTPS化直後の確認にも定番のツールです。
3. ブラウザのデベロッパーツール
ブラウザのF12→Networkタブ→ページのリクエストを選択→Headersタブで「Response Headers」を確認します。コードなしで誰でもすぐ確認できる最も手軽な方法です。設定漏れがあればどのヘッダーが欠けているかもひと目でわかります。
中小企業でも今日からできること
「Nginxの設定を触るのは怖い」という方も、以下の優先順位で着手してみてください。上位4つは設定値がほぼ固定で副作用のリスクも低く、まずここから始めるだけでセキュリティスコアが大きく改善します。
| 優先度 | ヘッダー | 防ぐ攻撃 | 設定難易度 |
|---|---|---|---|
| ★★★ | X-Content-Type-Options | MIMEスニッフィング | 低(値はnosniff固定) |
| ★★★ | X-Frame-Options | クリックジャッキング | 低(値はDENY推奨) |
| ★★★ | HSTS | HTTPSダウングレード・SSL Stripping | 低(HTTPS設定済みが前提) |
| ★★ | Referrer-Policy | リファラー経由の情報漏洩 | 低(推奨値で固定) |
| ★★ | Permissions-Policy | ブラウザAPI悪用 | 中(使用するAPIによって調整) |
| ★ | Content-Security-Policy | XSS(対策として最強) | 高(サイト環境に合わせた調整が必要) |
WordPressを利用している場合、Webサーバー設定が変更できないレンタルサーバー環境でも、テーマのfunctions.phpに以下を追記することでヘッダーを付与できます。
// functions.php に追記(サーバー設定変更ができない場合の代替手段) add_action('send_headers', function() { header('X-Content-Type-Options: nosniff'); header('X-Frame-Options: SAMEORIGIN'); header('Referrer-Policy: strict-origin-when-cross-origin'); header('Strict-Transport-Security: max-age=31536000; includeSubDomains'); });
ただし、WebサーバーレベルでHTTPヘッダーを設定する方法の方が確実で、PHPのオーバーヘッドも発生しません。サーバー設定変更が可能な環境ではそちらを優先してください。
Linuxサーバーのセキュリティ強化全般については、姉妹サイトLinuxMaster.JPでも詳しく解説しています。
よくある誤解と注意点
【誤解1】「セキュリティヘッダーを設定すれば完璧」
セキュリティヘッダーはあくまで多層防御(ディフェンス・イン・デプス)の一層です。アプリケーション側の入力バリデーション、CSRFトークンの実装、定期的なライブラリのアップデートなどと組み合わせてはじめて意味を持ちます。ヘッダーの設定はスタート地点であって、ゴールではありません。
【誤解2】「HSTS設定後に証明書が切れてもリダイレクトされるから問題ない」
HSTSが設定された状態で証明書が期限切れになると、ブラウザは「安全でない接続」として遮断し、ユーザーはサイトにアクセスできなくなります。HSTSを設定する際は、Let’s Encryptのcertbotなどによる証明書の自動更新を必ずセットで設定してください。
【誤解3】「CSPの unsafe-inline は絶対ダメ」
WordPressなどCMS環境では、テーマやプラグインがインラインスクリプトを多用しており、unsafe-inlineを禁止するとサイトの表示・機能が壊れることがあります。完全なCSP適用はコストが高く、まずX-Content-Type-Options・X-Frame-Options・HSTSの3つから始め、CSPはnonce対応の改修が可能な段階で段階的に追加するのが現実的な進め方です。
本記事のまとめ
HTTPSを設定しただけでは、ブラウザ上での攻撃シナリオには対応できません。セキュリティヘッダーを追加することで、XSS・クリックジャッキング・MIMEスニッフィング・HTTPSダウングレードといった攻撃リスクを大幅に下げられます。
| ヘッダー | 防ぐ主な攻撃 | 今すぐ設定すべきか |
|---|---|---|
| HSTS | HTTPSダウングレード・SSL Stripping | 即設定推奨 |
| X-Frame-Options | クリックジャッキング | 即設定推奨 |
| X-Content-Type-Options | MIMEスニッフィング | 即設定推奨 |
| Referrer-Policy | リファラー経由の情報漏洩 | 即設定推奨 |
| Permissions-Policy | ブラウザAPI悪用 | サイトに合わせて設定 |
| CSP | XSS(最も強力な対策) | Report-Onlyから段階的に |
設定後はcurl -sI https://your-domain.comコマンドやSecurityHeaders.comでスコアを確認し、抜け漏れがないか必ずチェックしてください。Webサーバーレベルでの設定が最も確実ですが、WordPress環境ではfunctions.phpからも設定可能です。まず上位4つのヘッダーから始め、CSPは環境を確認しながら段階的に追加していくのが、現場で確実に進められる方法です。
サーバーのセキュリティ設定、どこまで対応できていますか?
セキュリティヘッダーはほんの一例です。ファイアウォール・アクセス制御・ログ監視まで、確認すべき項目は多岐にわたります。
正しいセキュリティ知識を体系的に身につけたい方へ、メルマガで実践的なセキュリティ対策ノウハウをお届けしています。
