HTTP-заголовки безопасности для сайта
Вы защитили сервер, обновили фреймворк и обучили команду безопасному программированию. Но ваш сайт всё равно уязвим, если он не отправляет правильные HTTP-заголовки безопасности. Эти заголовки ответа сообщают браузерам, как обрабатывать ваш контент, а отсутствие или неправильная настройка заголовков открывает путь для межсайтового скриптинга (XSS), кликджекинга, атак с понижением протокола и утечек данных.
В этом руководстве мы разберём основные заголовки безопасности, которые должен отправлять каждый сайт, объясним, что делает каждый из них, и покажем, как их правильно настроить.
Почему заголовки безопасности важны
Заголовки безопасности — это первая линия защиты, поскольку они обеспечивают соблюдение политик на уровне браузера, которые контролируете вы. Они не заменяют валидацию входных данных или безопасную аутентификацию, но значительно сокращают поверхность атаки. Например, строгая Content-Security-Policy может остановить выполнение внедрённого скрипта, даже если злоумышленник нашёл уязвимость XSS.
Основные браузеры последовательно поддерживают эти заголовки, а их добавление обычно занимает несколько строк конфигурации. Вряд ли есть причина их не использовать.
Основные заголовки безопасности
Ниже перечислены заголовки, которые должен отправлять каждый продакшн-сайт. Мы рассмотрим, что они делают, рекомендуемые значения и типичные ошибки.
1. Content-Security-Policy (CSP)
CSP — самый мощный заголовок для противодействия XSS и инъекциям данных. Он ограничивает источники, из которых браузер может загружать скрипты, стили, изображения и другие ресурсы. Хорошая стартовая политика может выглядеть так:
Content-Security-Policy: default-src 'self'; script-src 'self' https://trusted.cdn.com; style-src 'self' 'unsafe-inline'; img-src 'self' data: https://images.example.com; object-src 'none'; base-uri 'self'; form-action 'self';Начните с default-src 'self' и постепенно добавляйте исключения по мере необходимости. По возможности избегайте 'unsafe-inline' для скриптов; используйте nonce или хеши. Тщательно тестируйте, потому что неправильно настроенный CSP может сломать ваш сайт.
2. HTTP Strict Transport Security (HSTS)
HSTS заставляет браузеры использовать HTTPS для всех будущих запросов к вашему домену. Это предотвращает атаки с понижением протокола и перехват cookie. Типичный заголовок:
Strict-Transport-Security: max-age=31536000; includeSubDomains; preloadmax-age указывается в секундах (один год). includeSubDomains применяет политику ко всем поддоменам. preload позволяет добавить ваш домен в списки предзагрузки браузеров, но используйте его только если вы абсолютно уверены, что все поддомены поддерживают HTTPS.
3. X-Frame-Options
Этот заголовок запрещает встраивать ваш сайт в iframe, что блокирует атаки кликджекинга. Используйте:
X-Frame-Options: DENYИли SAMEORIGIN, если вам нужно встраивать собственный контент. Современные браузеры также поддерживают директиву frame-ancestors в CSP, которая более гибкая. Если вы используете CSP, можно опустить X-Frame-Options, но указание обоих обеспечивает лучшую совместимость.
4. X-Content-Type-Options
Этот заголовок запрещает браузерам определять MIME-тип ответа вопреки объявленному Content-Type. Он прост и эффективен:
X-Content-Type-Options: nosniffБез него вредоносный файл может быть интерпретирован как исполняемый скрипт. Всегда устанавливайте его.
5. Referrer-Policy
Referrer-Policy управляет тем, сколько информации о реферере отправляется с запросами. Сбалансированное значение по умолчанию:
Referrer-Policy: strict-origin-when-cross-originЭто отправляет полный URL для запросов с того же источника и только origin для кросс-доменных запросов. Это снижает утечку конфиденциальной информации о путях, сохраняя аналитику.
6. Permissions-Policy
Ранее Feature-Policy, этот заголовок позволяет включать или отключать функции браузера, такие как геолокация, камера и микрофон. Пример:
Permissions-Policy: geolocation=(), camera=(), microphone=()Отключение неиспользуемых функций снижает последствия компрометации сторонних скриптов.
Сравнение заголовков безопасности
| Заголовок | Назначение | Рекомендуемое значение |
|---|---|---|
| Content-Security-Policy | Противодействие XSS и инъекциям данных | default-src 'self'; script-src 'self' ... |
| Strict-Transport-Security | Принудительное использование HTTPS | max-age=31536000; includeSubDomains |
| X-Frame-Options | Предотвращение кликджекинга | DENY или SAMEORIGIN |
| X-Content-Type-Options | Запрет MIME-сниффинга | nosniff |
| Referrer-Policy | Контроль утечки реферера | strict-origin-when-cross-origin |
| Permissions-Policy | Ограничение функций браузера | geolocation=(), camera=() |
Как добавить заголовки безопасности
Метод зависит от вашего веб-сервера или фреймворка. Вот распространённые подходы.
Nginx
Добавьте заголовки в блок server:
add_header Content-Security-Policy "default-src 'self'; script-src 'self' https://trusted.cdn.com; style-src 'self' 'unsafe-inline'; img-src 'self' data: https://images.example.com; object-src 'none'; base-uri 'self'; form-action 'self';" always;
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 "geolocation=(), camera=(), microphone=()" always;Параметр always гарантирует отправку заголовков даже при ответах с ошибками.
Apache
Включите mod_headers и добавьте:
Header always set Content-Security-Policy "default-src 'self'; script-src 'self' https://trusted.cdn.com; style-src 'self' 'unsafe-inline'; img-src 'self' data: https://images.example.com; object-src 'none'; base-uri 'self'; form-action 'self';"
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 "geolocation=(), camera=(), microphone=()"Node.js (Express)
Используйте middleware helmet, который по умолчанию устанавливает множество заголовков:
const helmet = require('helmet');
app.use(helmet());
// Customize CSP if needed
app.use(helmet.contentSecurityPolicy({
directives: {
defaultSrc: ["'self'"],
scriptSrc: ["'self'", "https://trusted.cdn.com"],
styleSrc: ["'self'", "'unsafe-inline'"],
imgSrc: ["'self'", "data:", "https://images.example.com"],
objectSrc: ["'none'"],
baseUri: ["'self'"],
formAction: ["'self'"],
}
}));Helmet также по умолчанию устанавливает другие заголовки, такие как X-Content-Type-Options и Referrer-Policy.
Тестирование заголовков
После развёртывания проверьте заголовки с помощью инструментов разработчика в браузере (вкладка Network) или онлайн-сканеров, таких как SecurityHeaders.com. Убедитесь, что:
- Все рекомендуемые заголовки присутствуют.
- CSP не блокирует легитимные ресурсы (проверьте консоль на нарушения).
- HSTS включён только если HTTPS полностью работает.
- Заголовки отправляются и на страницах ошибок.
Регулярно пересматривайте и обновляйте свои политики по мере развития сайта.
FAQ
Какой заголовок безопасности самый важный?
Content-Security-Policy часто считается самым важным, поскольку он напрямую противодействует XSS и инъекциям данных — одним из самых распространённых веб-уязвимостей.
Могут ли заголовки безопасности заменить другие меры защиты?
Нет. Заголовки безопасности — это один из слоёв эшелонированной защиты. Вам по-прежнему нужны безопасное программирование, валидация входных данных, аутентификация и другие лучшие практики.
Сломают ли заголовки безопасности мой сайт?
При неправильной настройке, особенно CSP, они могут блокировать легитимные ресурсы. Всегда тестируйте в staging-окружении и следите за консолью браузера на нарушения перед развёртыванием в продакшн.
Готовы защитить свой сайт? Начните с добавления заголовков выше, затем протестируйте с помощью инструментов разработчика в браузере. Чтобы быстро просмотреть и отформатировать JSON-ответы от вашего сканера безопасности, попробуйте наш JSON Formatter.