HSTS: как заставить браузер всегда использовать HTTPS

Как работает HSTS и заголовок Strict-Transport-Security, зачем нужны max-age, includeSubDomains и preload. Показываю инструмент для массовой проверки HSTS и ETag у списка URL

HSTS

Переход сайта на HTTPS сам по себе еще не означает, что пользователь никогда не обратится к нему по обычному HTTP. Старые ссылки, ручной ввод адреса, внешние ресурсы и сохраненные закладки могут по-прежнему вести на http://.

Обычно сервер решает эту проблему редиректом:

http://example.com/ - 301 https://example.com/

Но здесь остается небольшой, принципиально важный промежуток: первый запрос браузер все равно отправляет по HTTP. Именно эту проблему решает HSTS — HTTP Strict Transport Security.

HSTS позволяет сайту сообщить браузеру: этот домен необходимо открывать исключительно через HTTPS. После получения такой политики браузер начинает самостоятельно заменять HTTP на HTTPS еще до отправки запроса серверу.

Что такое HSTS

HSTS — механизм веб-безопасности, стандартизированный в RFC 6797. Сервер передает браузеру специальный HTTP-заголовок:

Strict-Transport-Security: max-age=31536000

Получив его через защищенное HTTPS-соединение, браузер запоминает правило для домена.

После этого обращение пользователя к:

http://example.com/page

может быть преобразовано браузером непосредственно в:

https://example.com/page

То есть сначала идти на HTTP-версию сайта и ждать серверного 301 уже не требуется.

Это принципиальное отличие HSTS от обычного HTTP - HTTPS редиректа.

Как работает HSTS

Упрощенно схема выглядит следующим образом.

При первом посещении сайта пользователь может обратиться по HTTP:

http://example.com

Сервер перенаправляет его:

301 → https://example.com

После перехода на HTTPS сервер возвращает:

Strict-Transport-Security: max-age=31536000

Браузер запоминает HSTS-политику. Если позже пользователь снова вводит:

http://example.com

браузер уже знает, что для домена разрешен только HTTPS, поэтому самостоятельно обращается к защищенной версии сайта.

Кроме того, для домена с действующей HSTS-политикой браузер значительно строже относится к ошибкам TLS-сертификата и не должен позволять пользователю просто проигнорировать предупреждение и продолжить работу через небезопасное соединение.

Зачем нужен HSTS, если уже настроен 301-редирект

На первый взгляд механизм действительно кажется избыточным. Если весь HTTP-трафик перенаправляется на HTTPS, зачем нужен еще один заголовок? Проблема заключается именно в первом HTTP-запросе. При обычной схеме:

пользователь

HTTP

сервер

301

HTTPS

браузер должен сначала обратиться к серверу через незашифрованное соединение. HSTS изменяет эту схему:

пользователь

браузер знает HSTS

HTTPS

сервер

HTTP-запрос вообще не выполняется. Механизм предназначен в том числе для защиты от атак, связанных с попыткой принудительно оставить пользователя на незашифрованной версии сайта.

Директива max-age

Основным параметром HSTS является max-age:

Strict-Transport-Security: max-age=31536000

Значение задается в секундах и определяет, как долго браузер должен считать домен HSTS-хостом. Например:

31536000 секунд = примерно 1 год

Каждый новый корректный ответ сайта с HSTS может обновлять этот срок. Значение:

Strict-Transport-Security: max-age=0

наоборот, сообщает браузеру, что сохраненную HSTS-политику для этого хоста необходимо удалить. Важно учитывать это при настройке: большое значение max-age фактически означает обязательство продолжать нормально обслуживать сайт по HTTPS в течение указанного периода. Поэтому экспериментировать сразу с очень большим значением на инфраструктуре, в стабильности которой нет уверенности, не лучшая идея.

includeSubDomains

Следующий вариант:

Strict-Transport-Security: max-age=31536000; includeSubDomains

распространяет политику не только на основной хост, но и на его поддомены. Это усиливает защиту, но одновременно требует осторожности. Предположим, существуют:

example.com
www.example.com
old.example.com
api.example.com

Если для example.com включить includeSubDomains, каждый из соответствующих поддоменов должен быть готов нормально работать через HTTPS.

Старый технический поддомен, внутренний сервис или забытый лендинг без корректного HTTPS после этого могут стать недоступны пользователям, браузеры которых уже получили HSTS-политику.

Поэтому includeSubDomains следует включать после проверки инфраструктуры, а не просто потому, что эта директива выглядит более надежной.

HSTS Preload

У обычного HSTS существует фундаментальное ограничение. Чтобы узнать о политике сайта, браузер сначала должен хотя бы один раз получить:

Strict-Transport-Security

через HTTPS.

Следовательно, для совершенно нового пользователя первое обращение по HTTP все еще потенциально возможно. Для решения этой проблемы существует механизм HSTS Preload. Некоторые браузеры поставляются со списком доменов, для которых HTTPS требуется заранее. Браузеру не нужно сначала посещать сайт и получать от него HSTS — информация уже находится в preload-списке.

Типичная конфигурация выглядит так:

Strict-Transport-Security: max-age=63072000; includeSubDomains; preload

Для включения домена в preload-лист действуют дополнительные требования. В частности, используется includeSubDomains, а max-age должен составлять не менее одного года.

Однако подключать preload без предварительного анализа особенно рискованно: политика распространяется гораздо жестче, а быстро отменить последствия неправильной настройки значительно сложнее.

Почему HSTS передается только через HTTPS

Есть еще одна важная особенность. Заголовок:

Strict-Transport-Security

имеет смысл только в ответе, полученном по HTTPS.

Если сервер возвращает его через обычный HTTP, браузер должен игнорировать такой заголовок.

Это логично: если бы HSTS-политику можно было устанавливать через незащищенное соединение, злоумышленник, способный вмешаться в HTTP-трафик, смог бы манипулировать самой политикой безопасности. Поэтому правильная схема обычно выглядит примерно так:

HTTP

301/308

HTTPS

Strict-Transport-Security

Примеры HSTS

Минимальная конфигурация:

Strict-Transport-Security: max-age=31536000

Политика сроком на год с распространением на поддомены:

Strict-Transport-Security: max-age=31536000; includeSubDomains

Вариант для инфраструктуры, подготовленной к HSTS Preload:

Strict-Transport-Security: max-age=63072000; includeSubDomains; preload

Последний вариант не стоит механически копировать на любой сайт. Сначала необходимо убедиться, что основной домен и требуемые поддомены стабильно работают по HTTPS.

Как проверить наличие HSTS

Для одной страницы проще всего посмотреть HTTP-заголовки ответа через инструменты разработчика браузера или выполнить запрос через консоль.

Например:

curl -I https://example.com/

В ответе нужно найти:

Strict-Transport-Security: ...

Но для одного сайта этого достаточно, а для десятков или сотен URL ручная проверка быстро превращается в рутину. Для таких задач я сделал небольшой скрипт hsts-etag-checker. Он предназначен для массовой проверки наличия HTTP-заголовков Strict-Transport-Security и ETag у списка URL.

Массовая проверка HSTS с помощью hsts-etag-checker

Логика работы максимально простая.

В файл:

urls.txt

помещаются адреса, которые необходимо проверить:

https://example.com
https://example.org
example.net

По одному URL на строку. Если схема не указана, скрипт автоматически добавляет:

https://

Пустые строки и строки-комментарии, начинающиеся с #, пропускаются.

После установки зависимостей:

pip install -r requirements.txt

запускается:

python check.py

Результат сохраняется в:

out.csv

Что находится в отчете

Для каждого URL выводятся четыре основных поля:

url
status
hsts
etag

Например:

url,status,hsts,etag
https://example.com,200,yes,yes
https://example.org,200,no,no

status показывает HTTP-код ответа либо информацию об ошибке.

hsts принимает значения:

yes
no

и показывает наличие заголовка Strict-Transport-Security.

Аналогичным образом etag показывает наличие заголовка ETag. Порядок строк в итоговом CSV сохраняется таким же, как во входном файле.

Зачем одновременно проверять ETag

ETag относится уже к другой задаче.

Заголовок:

ETag

используется в механизме HTTP-кэширования и помогает клиенту определить, изменилась ли версия ресурса. Поэтому HSTS и ETag технически не связаны между собой напрямую. В инструменте они объединены скорее практически: во время технического аудита можно одним проходом получить информацию сразу о нескольких HTTP-заголовках. Основной сценарий в контексте этой статьи — именно массовая проверка HSTS.

Параллельная проверка URL

При большом количестве адресов последовательное выполнение HTTP-запросов может занимать заметное время. Поэтому в скрипте используется параллельная обработка. Количество потоков задается параметром:

MAX_WORKERS

По умолчанию используется:

20

Также можно изменить таймаут запроса:

TIMEOUT

который по умолчанию составляет 10 секунд. Это особенно удобно, когда нужно проверить не несколько страниц, а большой список доменов или URL.

Что именно проверяет инструмент

Здесь важно понимать ограничение проверки.

Значение:

hsts = yes

означает, что в полученном ответе присутствует заголовок:

Strict-Transport-Security

Это еще не означает автоматически, что вся HSTS-конфигурация сайта идеальна.

Например, отдельно могут потребовать проверки:

  • значение max-age;
  • наличие includeSubDomains;
  • наличие preload;
  • HTTPS на всех поддоменах;
  • корректность TLS-сертификатов;
  • HTTP - HTTPS редиректы;
  • попадание домена в реальный HSTS preload list.

Поэтому скрипт правильнее рассматривать как инструмент первичной массовой диагностики. Он быстро отвечает на вопрос: «На каких URL присутствует HSTS, а на каких его нет?»

После этого проблемные или интересующие домены можно анализировать подробнее.

Где такая проверка может пригодиться

На практике массовая проверка HSTS удобна при техническом аудите большого количества сайтов или доменов. Например, если есть список из нескольких сотен сайтов, вручную открывать каждый домен и искать нужный заголовок бессмысленно. Гораздо проще сформировать:

urls.txt

запустить проверку и отфильтровать в CSV строки:

hsts = no

Таким образом можно быстро выделить сайты, требующие дополнительного анализа HTTPS-конфигурации. А поскольку одновременно собирается информация об ETag, тот же проход дает дополнительные данные о настройках HTTP-ответов.

Итог

HSTS — достаточно простой по форме, но важный по смыслу механизм.

Обычный HTTPS и HTTP - HTTPS редирект защищают соединение после перехода на защищенный протокол. HSTS идет дальше и позволяет браузеру запомнить, что обращаться к определенному домену через HTTP вообще не следует.

Базовая политика задается одним заголовком:

Strict-Transport-Security: max-age=31536000

а дополнительные директивы позволяют распространить ее на поддомены и подготовить домен к HSTS Preload.

При этом чем жестче политика, тем внимательнее необходимо проверять инфраструктуру перед ее включением. Для единичного сайта наличие HSTS легко проверить вручную. Если же нужно пройти десятки, сотни или тысячи URL, удобнее автоматизировать процесс. Для такой задачи можно использовать мой hsts-etag-checker — небольшой Python-скрипт, который принимает список URL и формирует CSV с HTTP-статусами и информацией о наличии HSTS и ETag.

Это не полноценный аудит безопасности и не валидатор всей HSTS-конфигурации, а простой инструмент первичной массовой проверки HTTP-заголовков — именно в этом сценарии он и наиболее полезен.