本文へスキップ
サイト総合診断

HSTS とは — HTTPS を強制して盗聴を防ぐ

HSTS は、レスポンスヘッダー Strict-Transport-Security によって「今後このドメインへは必ず HTTPS で接続する」とブラウザに記憶させる仕組みです。以後ブラウザは http:// のアクセスを内部で https:// に置き換え、最初の 1 回の平文通信を狙う SSL ストリッピング攻撃を防ぎます。効果は大きい一方、preload の登録は実質取り消せないため、書式と危険を理解してから入れる必要があります。

執筆: 公開:

HSTS とは何ですか?

HSTS は、サーバーが返すレスポンスヘッダー Strict-Transport-Security で、ブラウザに「今後 max-age 秒間、このドメインへは必ず HTTPS で接続する」と記憶させる仕組みです。以後ブラウザは http:// でのアクセスを内部で https:// に置き換えてから送信するため、平文の HTTP 通信そのものが発生しなくなります。

HTTPS 化そのものは「通信を暗号化する」対策ですが、HSTS はその一段手前、「そもそも暗号化されていない通信を発生させない」ための仕組みです。 サーバーが一度 Strict-Transport-Security ヘッダーを返すと、ブラウザはそのドメインを記憶し、 次からは利用者が http://example.com と打っても、リクエストを送る前に自分で https://example.com へ書き換えます。

つまり HSTS の効果はブラウザ側で強制されます。アプリのコードには手を入れず、レスポンスヘッダーを 1 行足すだけで、 平文通信という攻撃の足場を塞げるのが特徴です。前提として、サイトが完全に HTTPS で動いていることだけは満たしておく必要があります。

HSTS は何を防ぐのですか?

SSL ストリッピングという中間者攻撃を防ぎます。これは、利用者が最初に打つ http:// のアクセスを通信経路上で乗っ取り、HTTPS へのリダイレクトを握り潰して、平文のまま盗聴・改ざんする攻撃です。HSTS があれば 2 回目以降ブラウザは http を一切送らないため、この割り込みの起点そのものが消えます。

HTTPS へリダイレクトしているサイトでも、利用者が最初に打つ 1 回は http で始まることがほとんどです。 ブックマークや手打ちの URL、古いリンクは http:// のまま残っています。SSL ストリッピングは、この最初の 1 回を狙います。

  • 利用者が公衆 Wi-Fi などで http://example.com にアクセスする
  • 通信経路に割り込んだ攻撃者が、サーバーからの 「https へ移動せよ」というリダイレクトを握り潰す
  • 利用者のブラウザは http のまま通信を続け、入力内容も表示内容も平文で筒抜けになる

HSTS を一度受け取ったブラウザは、以後 http を一切送らなくなります。リクエストが送信される前に https へ置き換わるため、 攻撃者が割り込む起点そのものが消えます。これが HSTS の核心です。

HSTS の書式と各ディレクティブの意味は?

書式は Strict-Transport-Security: max-age=31536000; includeSubDomains; preload です。max-age は HTTPS 接続を記憶する秒数、includeSubDomains は全サブドメインへの HTTPS 強制、preload はブラウザ組み込みリストへの登録許可を表します。max-age は必須で、残り 2 つは任意かつ副作用が大きい指定です。

Strict-Transport-Security の書式
# HSTS は HTTPS のレスポンスに付ける(http では無視される)
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload

# 各部の意味
#   max-age=31536000  … 31536000 秒 = 365 日、HTTPS 接続を記憶する
#   includeSubDomains … 全サブドメインにも HTTPS を強制する
#   preload           … ブラウザ組み込みのプリロードリスト登録を許可する
HSTS の各ディレクティブと注意点
ディレクティブ意味注意
max-age=秒HTTPS 接続を記憶する秒数。必須長い値ほど強力だが、取り消しにその期間ぶん待たされる(max-age=0 で即時解除)
includeSubDomains全サブドメインにも HTTPS を強制http で動くサブドメインが 1 つでもあると到達不能になる
preloadブラウザ組み込みリストへの登録を許可解除は数か月かかり実質不可逆。安易に付けない

max-ageで指定します。31536000 は 365 日ぶんの秒数で、実務ではこの 1 年がよく使われます。 残りの 2 つは付けると効果が広がる反面、後述するように取り返しのつかない副作用を持つため、意味を理解してから足してください。

includeSubDomains を付けると何が起きますか?

そのドメインの全サブドメインが HTTPS 必須になります。api や staging、社内ツールなど http で動いているサブドメインが 1 つでもあると、その日から到達不能になります。付ける前に必ずサブドメインを棚卸しし、すべてが HTTPS で動くことを確認してください。ワイルドカード証明書があると移行が楽になります。

includeSubDomains は文字どおり、example.com だけでなく api.example.comblog.example.comstaging.example.com といったすべてのサブドメインに HTTPS を強制します。強力ですが、裏を返せばHTTPS 化できていないサブドメインを巻き込んで全滅させる指定でもあります。

preload はなぜ危険なのですか?

preload はブラウザ本体に設定が焼き込まれて配布されるため、解除が実質不可逆だからです。プリロードリストに登録されると、サーバーからヘッダーを消しても既に配布されたブラウザには残り、解除申請から各ブラウザへ行き渡るまで数か月かかります。運用が固まるまで付けてはいけません。

通常の HSTS は「そのブラウザが一度アクセスして初めて」効きます。preload はこの弱点を埋め、一度も訪れたことのないブラウザにも最初から HTTPS を強制する仕組みです。実現方法は、 ドメインを 主要ブラウザが共有するプリロードリストに登録し、ブラウザ本体にそのリストを焼き込んで配布するというものです。

HSTS を付ければ http→https のリダイレクトは不要ですか?

不要にはなりません。HSTS 自体はリダイレクトせず、あくまでブラウザ内部での置き換えを指示するだけです。そのドメインを初めて訪れるブラウザや、記憶が切れたブラウザは通常どおり http でアクセスするため、サーバー側の 301 リダイレクトは別途必要です。両者は役割が異なる補完関係にあります。

ここは混同されがちですが、HSTS とリダイレクトは別物です。HSTS は「ブラウザが記憶していれば、送信前に https へ置き換える」だけで、 サーバーへ http のリクエストが届いた場合に自動で飛ばす機能ではありません。次の 2 つは、担当する場面が違います。

  • 301 リダイレクト — サーバーが受けた http アクセスを https へ飛ばす。初回や記憶切れのブラウザを https に乗せるために必要
  • HSTS — 一度 https で受け取った記憶を使い、2 回目以降はそもそも http を送らせない

両方揃って初めて経路全体が守られます。リダイレクト側の実装はHTTP から HTTPS へのリダイレクト設定にまとめています。

HSTS はどう導入すればいいですか?

max-age=300(5 分)など短い値から始め、問題が出ないことを確認してから 31536000(1 年)へ伸ばします。前提としてサイトが完全に HTTPS で動いていることが必須です。長い max-age をいきなり配ると、設定ミスに気づいても記憶が切れるまでその期間ぶん待たされます。preload は最後に検討します。

nginx: max-age を段階的に伸ばす
# 段階的に伸ばす。いきなり 1 年を配らない(取り消せなくなる)

# 手順 1: まず短い max-age で様子を見る(5 分)
add_header Strict-Transport-Security "max-age=300" always;

# 手順 2: 問題が無ければ includeSubDomains を足して数日〜1 週間
add_header Strict-Transport-Security "max-age=604800; includeSubDomains" always;

# 手順 3: 安定したら 1 年へ。preload はさらに運用が固まってから検討する
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;

鉄則は「短い値から始めて、確認しながら伸ばす」ことです。いきなり max-age=31536000 を配ってしまうと、 設定ミスに気づいても、各利用者のブラウザの記憶が切れるまで最長 1 年間、その誤りが残り続けます。 最初を max-age=300(5 分)にしておけば、問題が起きても短時間で自然に解消できます。

HSTS が効いているかどう確認しますか?

curl -sI で HTTPS のレスポンスに Strict-Transport-Security ヘッダーが返っているかを確認します。HSTS ヘッダーは HTTPS のレスポンスでのみ有効で、http で送っても無視される点に注意してください。あわせて http アクセスが 301 で https へリダイレクトされることも確認すると、経路全体が検証できます。

curl で実際のヘッダーを確認する
# 1. HTTPS のレスポンスにヘッダーが付いているか(http では無視されるので https を見る)
curl -sI https://example.com | grep -i strict-transport-security

# 2. http アクセスが https へ 301 されるか(HSTS 自体はリダイレクトしない)
curl -sI http://example.com | grep -iE 'HTTP/|location'

最も多い失敗が、「設定したつもりで、実は返っていない」ケースです。特に HSTS ヘッダーはHTTPS のレスポンスでのみ有効で、http のレスポンスに付けてもブラウザに無視されます。 必ず https:// 側を curl -sI で叩き、Strict-Transport-Security の行が実際に返っていることを目視してください。

あわせて、http アクセスが 301 で https へ飛ぶことも確認すると、初回アクセスからの経路全体が検証できます。 ヘッダー設定を他のセキュリティヘッダーとまとめて見直すなら、セキュリティヘッダ 7 種の意味と設定もあわせて参照してください。

参考にした一次情報

関連する解説

自分のサイトは、実際どうなっているか

この記事で説明した項目は、すべて無料の診断ツールで実測できます。URL を入れるだけ・登録不要です。

無料で診断する