[Перевод] Нужные HTTP-заголовки
Наши клиенты в Fastly любят манипулировать заголовками HTTP. Подбор правильной комбинации заголовков — одна из лучших вещей, какую вы можете сделать для безопасности своего сайта и значительного вклада в его производительность.
Большинство разработчиков знают о важных и нужных HTTP-заголовках. Самые известные — Content-Type
и Content-Length
, это почти универсальные хедеры. Но в последнее время для повышения безопасности начали использоваться заголовки вроде Content-Security-Policy
и Strict-Transport-Security
, а для повышения производительности — Link rel=preload
. Несмотря на широкую поддержку в браузерах, лишь немногие их используют.
В предыдущей статье мы рассмотрели ненужные заголовки. Сейчас разберёмся, какие заголовки действительно следует настроить для своего сайта.
В интернете есть несколько сервисов, которые проанализируют ваш сайт и посоветуют, какие заголовки добавить. Я посмотрел securityheaders.io и Observatory от Mozilla, чтобы дополнить собственные знания и данные, полученные из сети Fastly.
Итак, какие основные заголовки должны быть в ответах ваших серверов? Бóльшая часть отвечает за повышение безопасности:
Content-Security-Policy. Действует как файрвол в браузере. Если ваш сайт скомпрометирован, помогает ограничить ущерб, предотвращая подключения к неодобренным хостам. Очень важный заголовок. Если у вас его нет, нужно включить.
Referrer-Policy. Настраивает уровень детализации для включения в заголовок Referer
при уходе со страницы. Помогает предотвратить утечку данных на сайты, куда идут ссылки. Настоятельно рекомендуется.
Strict-Transport-Security. Предотвращает любые попытки подключения к сайту по обычному HTTP. Помогает остановить MiTM-атаки и повышает безопасность сайта. Тоже настоятельно рекомендуется.
X-XSS-Protection. Предотвращает некоторые формы атак межсайтового скриптинга, предотвращая выполнение скрипта, если какая-то разметка из документа в той же форме присутствует в запросе. Например, если вы загружаете страницу с адресом /index.html?foo=
, а в исходном коде страницы есть , то скрипт блокируется. В наши дни заголовок во многом замещается CSP.
X-Content-Type-Options. Установите значение nosniff
, чтобы запретить браузерам выполнение контента, похожего на JavaScript, для которого не установлено правильное значение типа контента. Предотвращает атаки типа смешения MIME, а в последнее время используется Chrome для активации изоляции сайта. Со временем становится менее важным из-за улучшения поведения браузеров по умолчанию, но в настоящее время по-прежнему входит в число лучших практик.
CORS. Заголовки Cross-Origin Resource Sharing позволяют загружать URL скриптом с другого источника. Это необязательный заголовок. Заголовки такого типа являются разрешающими, а не запрещающими, поэтому их отсутствие даёт максимальный уровень безопасности.
Другие предназначены для производительности:
Timing-Allow-Origin. Даёт инструментам мониторинга доступ к данным по таймингу запроса. Это во многом отношении ценная информация, она позволяет сильно улучшить качество аналитики вроде Google Analytics или Speedcurve.
Link rel=preload. Сообщает браузеру о критических ресурсах, которые следует скачать, даже если непосредственной необходимости в них ещё не возникло. Используйте заголовок для шрифтов и важных CSS.
Server-Timing. Предоставляет с сервера информацию по таймингу, которая дополняет Navigation Timing API и Resource Timing API более детальной информацией о времени выполнения задач на сервере (например, «сколько времени мы провели в MySQL»). Отлично подходит для мониторинга данных о производительности, в сочетании с инструментами RUM Beacon.
Давайте более подробно рассмотрим некоторые из них.
Хотя Content-Security-Policy
— один из самых важных заголовков, он также один из самых многословных. Самый большой заголовок CSP, который я нашёл в HTTPArchive, занимал 10 КБ. Десять килобайт. Для одного значения заголовка. Хуже того: в то время как тела ответов могут передаваться потоком, заголовки буферизуются большинством серверов и прокси-серверов и передаются только после завершения. Сжатие HTTP/2 немного помогает запоминать их между запросами, но это не означает, что заголовок 10 КБ — это нормально.
Кроме того, заполняя весь первый пакет вашего ответа, вы можете заставить браузер совершить два обращения на сервер просто для начала получения контента. Так что думайте не только об удалении ненужных заголовков, но и о максимальной оптимизации существующих.
С незапамятных времён (то есть в веб-мире примерно с конца 90-х) браузеры отправляют (и неправильно пишут) заголовок Referer
. На протяжении большей части своей истории это был один из самых важных способов отслеживать перемещения пользователей между страницами в инструментах аналитики, а также понять происхождение входящего трафика. Тем не менее, последнее связано со значительными проблемами конфиденциальности. Если я нажму ссылку в письме из клиента электронной почты, то сайт может определить мой домен электронной почты. Хуже того, знание полного URL-адреса, с которого вы пришли, включая аргументы запроса, может раскрыть термины вашего последнего поискового запроса или личные данные, такие как адрес электронной почты.
Можете выбрать несколько стратегий из доступных вариантов Referrer-Policy, но мой обычный совет — это «origin-when-cross-origin», который включает Referer для всех нормальных запросов, но усекает значение только до домена, если ссылка идёт от одного домена к другому. Ссылки в пределах вашего собственного сайта включают полный реферер.
У Server-Timing
есть много приятных особенностей, а одна из них в том, что в ответ можете добавить несколько экземпляров — и все они объединятся в браузере или инструменте RUM. То есть если запрос проходит через несколько этапов серверной обработки — как это происходит в CDN — каждый этап добавляет собственные метрики времени, и они не конфликтуют между собой. Вот как добавить метрики Fastly в заголовок с помощью VCL в конфигурации службы Fastly:
add resp.http.Server-Timing = fastly_info.state {", fastly;desc="Edge time";dur="} time.elapsed.msec;
Это число включает время, потраченное на ожидание бэкенда, так что в обычном случае все ваши метрики серверного времени будут меньше, чем число Fastly. Тем не менее, если мы выдаём документ из кэша Fastly, вы увидите исходные метрики времени с момента генерации страницы, но число Fastly подтвердит, что общие потери времени на самом деле очень малы.
Метрики времени сервера доступны через объект performance в JavaScript и отображаются в сетевой панели Chrome Devtools:
В настоящее время визуализация показателей на рудиментарном уровне. Но над этим усиленно работают, так что в будущем пользовательский интерфейс, вероятно, значительно улучшится.
Fastly — хорошее место для добавления всех заголовков безопасности и производительности. Ниже показано, как они выглядит все вместе. Добавьте этот код на этапе доставки (deliver) потока запросов, изменив значения на те, которые подходят для вашего сайта (постарайтесь не копипастить без проверки, что значения подходят для вашего сайта):
set resp.http.Content-Security-Policy = "default-src 'self'; frame-ancestors 'self'";
set resp.http.Referer-Policy = "origin-when-cross-origin";
set resp.http.Strict-Transport-Security = "max-age=86400"; # Increase when working well in prod (a year is a common final value)
set resp.http.X-XSS-Protection = "1; mode=block";
set resp.http.X-Content-Type-Options = "nosniff";
if (req.http.Origin) { # Consider checking this against an allowlist
set resp.http.Access-Control-Allow-Origin = req.http.Origin;
set resp.http.Access-Control-Allow-Methods = "GET,HEAD,POST,OPTIONS";
}
add resp.http.Server-Timing = fastly_info.state {", fastly;desc="Edge time";dur="} time.elapsed.msec;
set resp.http.Timing-Allow-Origin = "*";
set resp.http.Link = "; rel=preload; as=font";
В следующей статье рассмотрим некоторые из самых экзотических заголовков, которые начинают стандартизировать и внедрять в браузеры.