Заголовки безопасности HTTP

2026-07-20 · 10 мин. для прочтения
blog computer-science

Заголовки безопасности HTTP.

Содержание

1 Типы проблем безопасности

  • Отсутствие или неправильная настройка HTTP-заголовков безопасности создаёт конкретные векторы атак.
Таблица 1: Таблица рисков
ЗаголовокПоследствие отсутствияПоследствие неправильного наличия
X-Frame-OptionsКликджекингБлокировка легитимных фреймов или слабая защита
X-Content-Type-OptionsXSS через загрузку файловОбычно безопасен
X-XSS-ProtectionСнижение защиты для старых броузеровМожет вызвать блокировку страниц
HSTSSSL-стриппингНедостаточное время жизни, проблемы с откатом
CSPXSS-атаки всех видовСломанный сайт или бесполезная политика
Referrer-PolicyУтечка приватных данных в URLСломанная аналитика или передача слишком много данных
Server / X-Powered-ByБезопасноРаскрытие информации о версиях

1.1 X-Frame-Options

1.1.1 Отсутствие

  • Сайт становится уязвимым к кликджекингу (clickjacking).
  • Злоумышленник встраивает вашу страницу в <iframe> на своём сайте и накладывает поверх неё прозрачные элементы.
  • Пользователь думает, что кликает по кнопке на сайте злоумышленника, а на самом деле взаимодействует с вашим сайтом (например, переводит деньги, меняет пароль).
  • Без этого заголовка броузер не запрещает встраивание в фреймы.

1.1.2 Наличие

  • X-Frame-Options: ALLOW-FROM https://bad-site.com — устаревший и плохо поддерживаемый параметр.
  • Если установлено DENY — можно случайно заблокировать легитимные встраивания (например, виджеты на партнёрских сайтах).
  • Если SAMEORIGIN — не защищает от атак через поддомены (если они скомпрометированы).

1.2 X-Content-Type-Options

1.2.1 Отсутствие

  • Броузер может выполнять MIME-сниффинг (автоопределение типа содержимого).
  • Если злоумышленник загрузит на ваш сервер файл с расширением .jpg, но с содержимым HTML/JavaScript, броузер может распознать его как скрипт и выполнить в контексте вашего домена.
  • Это приводит к XSS-атакам через загрузку файлов (например, в аватарках).

1.2.2 Наличие

  • Проблем нет, если установлено nosniff.
  • Это строгое требование доверять заголовку Content-Type, что полностью предотвращает сниффинг.

1.3 X-XSS-Protection

1.3.1 Отсутствие

  • В старых броузерах (Internet Explorer, Safari, Chrome до версии 78) была встроенная защита от отражённого XSS.
  • Если её отключить или не установить заголовок, броузер может не включить эту защиту.
  • Однако в современных броузерах этот заголовок устарел и игнорируется, поэтому основной риск — для устаревших клиентов.

1.3.2 Наличие

  • X-XSS-Protection: 0 — отключает защиту, что плохо.
  • X-XSS-Protection: 1; mode=block — включает защиту с блокировкой страницы.

1.3.3 Проблема

  • Сам механизм защиты может создавать уязвимости (например, DOM-based XSS с использованием mode=block), поэтому современные рекомендации советуют удалить этот заголовок и полагаться на CSP.
  • Но в корпоративных средах со старыми броузерами его временно оставляют.

1.4 Strict-Transport-Security (HSTS)

1.4.1 Отсутствие

  • Пользователь может быть атакован через SSL-стриппинг (злоумышленник перехватывает первый HTTP-запрос и перенаправляет на поддельный сайт без HTTPS).
  • Даже если у вас включён HTTPS, первый запрос может быть по HTTP, и атакующий может вмешаться до переадресации.
  • Без HSTS броузер не запоминает, что к вашему сайту нужно ходить только по HTTPS.

1.4.2 Наличие

  • Слишком короткий max-age (например, 5 минут) не даёт достаточной защиты.
  • Если не включён includeSubDomains, то поддомены остаются незащищёнными.
  • Ошибка в настройке может привести к тому, что броузер будет отказываться подключаться к сайту по HTTP, что сломает доступ для пользователей, если у них нет HTTPS.
  • Если вы установите preload и отправите домен в список HSTS preload, вы не сможете легко отказаться от HTTPS в будущем.

1.5 Content-Security-Policy (CSP)

1.5.1 Общая информация

  • CSP — это белый список для броузера.
  • Разрешает работать с ресурсами только из списка.
  • Для защиты от XSS-атак (межсайтовый скриптинг).
  • Даже если хакер внедрит свой скрипт на вашу страницу, броузер не выполнит его, потому что его нет в белом списке.

1.5.2 Отсутствие

  • Отсутствие CSP позволяет выполнять любой инлайн-скрипт и загружать ресурсы с любых источников.
  • Это делает сайт максимально уязвимым для XSS-атак любого типа (отражённых, хранимых, DOM-based).
  • Злоумышленник, внедривший скрипт, сможет украсть куки, сессии, данные форм и т.д.

1.5.3 Наличие

  • Слишком слабая политика (например, default-src * или script-src 'unsafe-inline') почти не даёт защиты (эквивалентно отсутствию).
  • Если установить слишком строгую политику, то сайт перестаёт работать: не грузятся скрипты, стили, картинки, шрифты с CDN, блокируются виджеты, API. Это приводит к нарушению функционирования сайта, но не к уязвимости.
  • Неправильные директивы для frame-ancestors могут разрешить кликджекинг (если не используется X-Frame-Options).
  • Отсутствие report-uri или report-to мешает отслеживать нарушения и корректировать политику.

1.6 Referrer-Policy

1.6.1 Отсутствие

  • Броузер по умолчанию передаёт полный URL предыдущей страницы в заголовке Referer.
  • Это может раскрывать чувствительные данные в строке запроса (например, ?token, ?sessionid) при переходе на сторонние сайты.
  • Утечка таких данных может привести к компрометации сессий.

1.6.2 Наличие

  • Неправильная политика, например no-referrer, может сломать аналитику или логику, зависящую от источника перехода.
  • unsafe-url передаёт полный URL даже внутри сайта.
  • Рекомендуется strict-origin-when-cross-origin (передаёт только домен для кросс-доменов и полный для своих), это безопасно.

1.7 Server и X-Powered-By

1.7.1 Наличие

  • Раскрытие информации.
  • Заголовок Server: Apache/2.4.54 (Ubuntu) сообщает злоумышленнику точную версию веб-сервера и ОС.
  • Заголовок X-Powered-By: PHP/8.1.2 раскрывает версию интерпретатора.
  • Это помогает атакующему подобрать известные эксплойты именно для версии сайта.

1.7.2 Отсутствие

  • Усложняет исследование сайта.
  • Однако их отсутствие не создаёт прямой уязвимости, но скрытие уменьшает поверхность атаки.

1.8 Приоритеты безопасности

  • Критично (высокий приоритет):

    • Добавить Strict-Transport-Security (HSTS) на всех HTTPS-портах.
    • Добавить X-Content-Type-Options: nosniff (защита от MIME-атак).
    • Убрать Server и X-Powered-By (скрытие информации).
  • Важно (средний приоритет):

    • Добавить X-Frame-Options (защита от кликджекинга).
    • Добавить Content-Security-Policy (защита от XSS, но требует тонкой настройки, чтобы не сломать сайт).
  • Рекомендуется (низкий приоритет):

    • X-XSS-Protection (устаревает в современных броузерах, но полезен как запасной вариант).
    • Referrer-Policy (контроль конфиденциальности).

1.9 Риски массового применения (через общий конфиг)

  • Если применять одни и те же заголовки ко всем виртуальным хостам, возможны следующие проблемы:
    • CSP для одного сайта может оказаться слишком строгим для другого (например, на одном используются внешние скрипты, а на другом — нет).
    • HSTS на поддоменах (includeSubDomains) может заставить все поддомены работать только по HTTPS, что может быть проблематично, если некоторые из них используют HTTP для внутренних целей.
    • Referrer-Policy может повлиять на работу аналитических систем.

2 Устранение недостатков

  • Все проблемы решаются настройкой веб-сервера (Nginx, Apache, IIS).

2.1 Добавить недостающие заголовки безопасности

  • В зависимости от вашего веб-сервера, добавьте следующие директивы:

  • Для Nginx (в блоке server):

    add_header X-XSS-Protection "1; mode=block" always;
    add_header X-Content-Type-Options "nosniff" always;
    add_header X-Frame-Options "SAMEORIGIN" always;
    add_header Referrer-Policy "strict-origin-when-cross-origin" always;
    add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; font-src 'self'; connect-src 'self';" always;
    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always; # ТОЛЬКО ДЛЯ HTTPS
    
  • Для Apache (в .htaccess или конфиге):

    Header always set X-XSS-Protection "1; mode=block"
    Header always set X-Content-Type-Options "nosniff"
    Header always set X-Frame-Options "SAMEORIGIN"
    Header always set Referrer-Policy "strict-origin-when-cross-origin"
    Header always set Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; font-src 'self'; connect-src 'self';"
    Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" env=HTTPS
    

2.2 Убрать информативные заголовки (Server, X-Powered-By)

  • Эти заголовки раскрывают версию ПО, что помогает злоумышленникам.

  • Nginx: В nginx.conf в секции http добавьте server_tokens off;.

  • Apache: Включите ServerSignature Off и ServerTokens Prod.

  • Убрать X-Powered-By:

    • PHP: expose_php = Off в php.ini.
    • Python/Flask: Используйте app.config['PROPAGATE_EXCEPTIONS'] = True или удалите заголовок в middleware.
    • Java: Настройте server.servlet.session.cookie или используйте фильтр для удаления заголовка.

2.3 Настройка CSP (Content-Security-Policy)

2.3.1 Пример работы

  • Пусть заголовок содержит: default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; ...

  • Это означает:

    • default-src 'self' : все ресурсы по умолчанию грузить только со своего домена.
    • 'unsafe-inline' : разрешить выполнять скрипты и стили, написанные прямо в HTML.
  • Шаблон блокирует работу, если на сайте есть:

    • Яндекс.Метрика или Google Analytics (скрипты грузятся с их серверов).
    • Видео с YouTube или Vimeo (iframe).
    • Шрифты Google Fonts (загружаются с fonts.googleapis.com).
    • Картинки с внешних CDN.
    • Виджеты или API (подключение к сторонним сервисам).
  • Например, броузер увидит попытку загрузить скрипт с https://mc.yandex.ru и заблокирует его, потому что в политике разрешен только 'self' (свой хост).

2.3.2 Настройка под сайт

  • Необходимо предварительно исследовать работу сайта.
  1. Режим отчетов (Report-Only)

    • В Apache используется Header set Content-Security-Policy-Report-Only.
    • В этом режиме броузер не блокирует ресурсы, а пишет в отлабочную консоль ошибки.
    # Включаем безопасный режим наблюдения, чтобы ничего не сломать
    Header always set Content-Security-Policy-Report-Only "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; font-src 'self'; connect-src 'self';"
    
  1. Консоль разработчика

    • Откройте сайт.
    • Нажмите F12 в броузере (Chrome/Firefox), перейдите на вкладку Console (Консоль).

    Вы увидите красные ошибки типа:

    Refused to load the script ‘https://mc.yandex.ru/metrika/tag.js’ because it violates the following Content Security Policy directive: “script-src ‘self’ ‘unsafe-inline’”.

  1. Правка шаблона

    • Глядя на ошибки, добавляйте недостающие домены в нужные директивы.
      • Шаблон: script-src 'self' 'unsafe-inline';

      • Видим ошибку с Яндекс.Метрикой:

        • Добавляем https://mc.yandex.ru https://yandex.ru: script-src 'self' 'unsafe-inline' https://mc.yandex.ru https://yandex.ru;
      • Видим ошибку с Google Fonts (стили и шрифты):

        • Добавляем в style-src и font-src: style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; font-src 'self' https://fonts.gstatic.com;
      • Видим ошибку с загрузкой картинок с внешнего CDN:

        • Добавляем в img-src: img-src 'self' data: https://cdn.myhost.ru;
  1. Включайте политику

    • Когда в режиме Report-Only консоль чиста, меняете название заголовка с Content-Security-Policy-Report-Only на Content-Security-Policy (убираете -Report-Only).
    • Теперь защита работает.

2.4 Конфигурационный файл

2.4.1 Apache

  1. Общий конфигурационный файл

    • Стандартные каталоги Apache для хранения общих конфигураций:
    ДистрибутивПуть для общих конфигураций
    Debian/Ubuntu/etc/apache2/conf-available/, /etc/apache2/conf.d/
    RHEL/CentOS/Fedora/etc/httpd/conf.d/
  1. Способ 1: Создать файл в conf.d / conf-available

    • Создайте файл с заголовками безопасности

      • Debian/Ubuntu:

        sudo touch /etc/apache2/conf-available/security-headers.conf
        
      • RHEL/CentOS:

        sudo touch /etc/httpd/conf.d/security-headers.conf
        
    • Добавьте нужные заголовки:

      # security-headers.conf — общие заголовки безопасности для всех виртуальных хостов
      
      # Защита от кликджекинга
      Header always set X-Frame-Options "SAMEORIGIN"
      
      # Защита от MIME-атак
      Header always set X-Content-Type-Options "nosniff"
      
      # Защита от XSS (для старых браузеров)
      Header always set X-XSS-Protection "1; mode=block"
      
      # Контроль Referer
      Header always set Referrer-Policy "strict-origin-when-cross-origin"
      
      # HSTS — ТОЛЬКО ДЛЯ HTTPS (проверьте, что у вас включён HTTPS)
      Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
      
      # CSP (настройте под свой сайт)
      Header always set Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; font-src 'self'; connect-src 'self';"
      
      # Скрываем версию сервера (глобально, в httpd.conf/apache2.conf)
      ServerTokens Prod
      ServerSignature Off
      
    • Подключите файл

      • Debian/Ubuntu

        sudo a2enconf security-headers
        sudo systemctl reload apache2
        
        • Или вручную добавьте в /etc/apache2/apache2.conf:
          IncludeOptional /etc/apache2/conf-available/security-headers.conf
          
      • RHEL/CentOS

        • Файлы в conf.d/ подключаются автоматически.
        • Перезагрузите Apache:
          sudo systemctl reload httpd
          
  1. Способ 2: Использовать .htaccess

    • Если включена обработка .htaccess, можно разместить общие заголовки в корневом .htaccess сайта.

    • .htaccess снижает производительность, так как Apache проверяет его при каждом запросе.

    • Для продакшена лучше использовать основной конфиг.

    • Файл: /var/www/html/.htaccess или /путь_к_сайту/.htaccess

      # .htaccess с общими заголовками безопасности
      Header always set X-Frame-Options "SAMEORIGIN"
      Header always set X-Content-Type-Options "nosniff"
      Header always set X-XSS-Protection "1; mode=block"
      Header always set Referrer-Policy "strict-origin-when-cross-origin"
      Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
      
  1. Способ 3: Подключить общий файл в каждом виртуальном хосте

    • Если нужно, чтобы разные виртуальные хосты использовали разные общие файлы:
      <VirtualHost *:443>
          ServerName site1.example.com
          DocumentRoot /var/www/site1
      
          # Подключаем общий файл с заголовками
          Include /etc/apache2/conf-available/security-headers.conf
      
          # Дополнительные настройки для этого хоста
          SSLEngine on
          SSLCertificateFile /path/to/cert.pem
      </VirtualHost>
      
  1. Проверка конфигурации

    • Проверьте синтаксис конфигурации:

      sudo apachectl configtest
      # или
      sudo httpd -t
      
    • Проверьте заголовки в ответе сервера:

      curl -I http://ваш-сервер:порт
      curl -I https://ваш-сервер:443 -k
      
      • В выводе должны появиться все добавленные заголовки.
    • Проверьте, что конфигурация загружена:

      # Debian/Ubuntu
      sudo apache2ctl -t -D DUMP_CONFIG | grep -i "security-headers"
      
      # RHEL/CentOS
      sudo httpd -t -D DUMP_CONFIG | grep -i "security-headers"
      

3 Проверка

3.1 Ручная проверка через командную строку

  • Используйте curl для каждого IP и порта, чтобы увидеть заголовки ответа:

    # Для HTTP
    curl -I http://hostname:80
    
    # Для HTTPS
    curl -I https://hostname:443 -k
    
  • Ожидаемый результат:

    HTTP/1.1 200 OK
    Server: nginx                         # <--- Если оставили, то скрыли версию
    X-XSS-Protection: 1; mode=block
    X-Content-Type-Options: nosniff
    X-Frame-Options: SAMEORIGIN
    Referrer-Policy: strict-origin-when-cross-origin
    Content-Security-Policy: default-src 'self'; ...
    Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
    

3.2 Использование онлайн-инструментов

3.2.1 SecurityHeaders.com

  • Сайт блокирует доступ из России.
  • Перейдите на сайт SecurityHeaders.com.
  • Введите URL вашего сервера.
  • Он выдаст оценку (A+, A, B и т.д.) и покажет, какие заголовки все еще отсутствуют.

3.3 Расширения для броузера

3.3.1 Web Security Headers Checker

3.4 Скрипт автоматической проверки

  • bash-скрипт для проверки всех IP и портов:
    #!/bin/bash
    hosts=("host:80" "host:443" "host:3128")
    for host in "${hosts[@]}"; do
        echo "Checking $host"
        curl -s -I "http://$host" | grep -E "X-XSS-Protection|X-Content-Type-Options|X-Frame-Options|Referrer-Policy|Content-Security-Policy|Strict-Transport-Security|Server|X-Powered-By"
        echo "---"
    done
    
Дмитрий Сергеевич Кулябов
Authors
Профессор кафедры теории вероятностей и кибербезопасности
Работаю профессором на кафедре теории вероятностей и кибербезопасности Российского университета дружбы народов им. Патриса Лумумбы. Научные интересы относятся к области теоретической физики и математического моделирования.