Компании, продающие "системы показателей безопасности", сейчас на подъеме, их влияние в сфере корпоративных продаж растет. К тому же есть те, кого низкий рейтинг безопасности у продавцов смущает, и те, кто хотя бы однажды, глядя на рейтинг, отказался от покупки, — я с такими людьми общался.


Я посмотрел, как эти компании вычисляют показатели безопасности других компаний. Оказалось, они смотрят на сочетание использования НТТР-заголовка для безопасности и репутации IP-адресов.


Репутация IP-адреса основывается на данных черных списков и списков спамеров в сочетании с данными о владельце общедоступного IP-адреса. Она, в принципе, должна быть чистой, если ваша компания не рассылает спам и в состоянии быстро определить и остановить вредоносное внедрение. Использование заголовка безопасности НТТР вычисляется аналогично тому, как работает Observatory от Mozilla.


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


Настроить заголовок правильно — это недолго (серьезной проверки не потребуется), но это улучшит безопасность сайта и поможет не растерять покупателей, для которых безопасность — не пустой звук


В ценности упомянутой методики проверки я сомневаюсь, к том же ценник за услугу выставлен заоблачный. Вряд ли компании-поставщики этой услуги способны определить безопасность ресурса с заявленной точностью. Впрочем, ситуация лишний раз подчеркивает, как важно сесть и уделить время тому, чтобы включить и настроить правильные заголовки.


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


Важные заголовки для безопасности


Content-Security-Policy


CSP применяется, чтобы предотвращать межсайтовый скриптинг — путем определения, какие ресурсы могут быть загружены. Из всего списка этот заголовок отнимет больше остальных времени на создание и правильную поддержку, а еще больше других подвержен рискам. Разрабатывая CSP, тщательно его проверяйте — если вдруг заблокируете используемый вами же источник, то нарушите функциональность собственного сайта.


Для предварительной версии можно использовать замечательный инструмент — расширение для браузера Mozilla, Laboratory CSP. Установите его в браузере, тщательно изучите сайт, для которого хотите создать CSP, а после используйте сгенерированную CSP на своем сайте. В идеале, нужно еще переработать JavaScript, так что можно удалить директиву «unsafe inline».


CSP может показаться сложной и сбить с толку, поэтому, если хотите углубиться в тему, посетите официальный сайт.


Предварительно можно настроить CSP так (на боевом сайте она, скорее всего, потребует множества модификаций). Добавьте в каждый раздел вашего сайта домены.


# Default to only allow content from the current site
# Allow images from current site and imgur.com
# Don't allow objects such as Flash and Java
# Only allow scripts from the current site
# Only allow styles from the current site
# Only allow frames from the current site
# Restrict URL's in the <base> tag to current site
# Allow forms to submit only to the current site
Content-Security-Policy: default-src 'self'; img-src 'self' https://i.imgur.com; object-src 'none'; script-src 'self'; style-src 'self'; frame-ancestors 'self'; base-uri 'self'; form-action 'self';

Strict-Transport-Security


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


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

X-Content-Type-Options


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


Также он снижает риск неожиданного поведения приложения, когда браузер неверно "угадывает" тип контента на сайте — например, если разработчик обозначает страницу "HTML", а браузер видит JavaScript и пытается отрисовать страницу соответственно. Также благодаря этому заголовку браузер всегда держится установленных сервером MIME-типов.


X-Content-Type-Options: nosniff

Cache-Control


Этот будет позаковыристее прочих, потому что для разных типов контента вам наверняка нужны разные политики кэширования.


Никакая деликатная информация — вроде страницы пользователя или страницы оплаты товара — не должна кэшироваться. Одна из причин для этого — чтобы другой пользователь компьютера не нажал кнопку "назад", не прочел историю и не увидел личных данных другого пользователя.


Впрочем, кэшировать можно и нужно те страницы, которые обновляются редко, например статические ресурсы (картинки, файлы CSS и JS). Кэширование можно настроить на постраничной основе, или используя regex в настройках сервера.


# Don’t cache by default
Header set Cache-Control no-cache

# Cache static assets for 1 day
<filesMatch ".(css|jpg|jpeg|png|gif|js|ico)$">
    Header set Cache-Control "max-age=86400, public"
</filesMatch>

Expires


Этот заголовок устанавливает время, на которое текущий запрос сохраняется в кэше. Он игнорируется, если включен заголовок Cache-Control max-age, так что мы включаем его только на случай, если его проверяет простенький сканер — без учета контроля кэширования.


Мы предполагаем, что в целях безопасности браузер не кэширует ничего, так что дата в заголовке всегда будет в прошлом.


Expires: 0

X-Frame-Options


Этот заголовок разрешает отображение сайта в iFrame.


Поместив ваш веб-сайт в iFrame, вредоносный ресурс получает возможность произвести кликджекинг атаку — запустив некий JavaScript, который обманом вынудит пользователя кликнуть по iFrame, а после начнет взаимодействовать с ресурсом от его, пользователя, имени (то есть человек кликнет по вредоносной ссылке или кнопке, даже не подозревая об этом!).


Этот заголовок всегда надо настраивать на отказ, исключение — если вы намеренно используете фреймы. Тогда заголовок нужно настраивать на тот же источник. Если вы по умолчанию используете фреймы с другим сайтом, занесите сторонний домен в белый список.


Следует также отметить, что этот заголовок замещается директивой CSP frame-ancestors. Я его рекомендую пока включать, но только для того, чтобы заткнуть инструменты для проверки заголовков, в будущем от него скорее всего избавятся.


X-Frame-Options: deny

Access-Control-Allow-Origin


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


Например, сайт А содержит некий JavaScript, который хочет сделать запрос к сайту В. Сайт В должен ответить на этот запрос — если заголовок разрешает сайту А сделать запрос. Если нужно настроить множество источников, подробности на странице на MDN.


Тут можно слегка запутаться, поэтому я составил схему — проиллюстрировать, как работает этот заголовок:



Поток данных с Access-Control-Allow-Origin


Access-Control-Allow-Origin: http://www.one.site.com


Убедитесь, что ваши cookies устанавливаются только через протокол HTTPS (с шифрованием), и что к ним нет доступа через JavaScript. Эти файлы можно посылать, только если ваш сайт тоже поддерживает HTTPS, как и должно быть. Всегда нужно выставлять вот такие флаги:


  • Secure
  • HTTPOnly

Пример определения Cookie:


Set-Cookie: <cookie-name>=<cookie-value>; Domain=<domain-value>; Secure; HttpOnly

Подробнее — в отличной документации по cookies от Mozilla.


X-XSS-Protection


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


X-XSS-Protection: 1; mode=block

Пример настроек веб-сервера


Вообще, в настройках сервера лучше всего добавлять заголовки на весь сайт. Исключение — файлы cookie, поскольку они определяются в самом приложении.


Советую, прежде чем добавить на сайт заголовки, сперва свериться с Observatory или вручную заглянуть в заголовки — проверить, какие уже установлены. Некоторые движки сайтов и сервера самостоятельно все установят, поэтому просто реализуйте нужные вам заголовки или измените те, которые в этом нуждаются.


Конфигурация Apache


Пример настройки Apache в .htaccess:


<IfModule mod_headers.c>
    ## CSP
    Header set Content-Security-Policy: default-src 'self'; img-src 'self' https://i.imgur.com; object-src 'none'; script-src 'self'; style-src 'self'; frame-ancestors 'self'; base-uri 'self'; form-action 'self';

    ## General Security Headers
    Header set X-XSS-Protection: 1; mode=block
    Header set Access-Control-Allow-Origin: http://www.one.site.com
    Header set X-Frame-Options: deny
    Header set X-Content-Type-Options: nosniff
    Header set Strict-Transport-Security: max-age=3600; includeSubDomains

    ## Caching rules
    # Don’t cache by default
    Header set Cache-Control no-cache
    Header set Expires: 0

    # Cache static assets for 1 day
    <filesMatch ".(ico|css|js|gif|jpeg|jpg|png|svg|woff|ttf|eot)$">
        Header set Cache-Control "max-age=86400, public"
    </filesMatch>

</IfModule>

Конфигурация Nginx


## CSP
add_header Content-Security-Policy: default-src 'self'; img-src 'self' https://i.imgur.com; object-src 'none'; script-src 'self'; style-src 'self'; frame-ancestors 'self'; base-uri 'self'; form-action 'self';

## General Security Headers
add_header X-XSS-Protection: 1; mode=block;
add_header Access-Control-Allow-Origin: http://www.one.site.com;
add_header X-Frame-Options: deny;
add_header X-Content-Type-Options: nosniff;
add_header Strict-Transport-Security: max-age=3600; includeSubDomains;

## Caching rules
# Don’t cache by default
add_header Cache-Control no-cache;
add_header Expires: 0;

# Cache static assets for 1 day
location ~* \.(?:ico|css|js|gif|jpe?g|png|svg|woff|ttf|eot)$ {
    try_files $uri @rewriteapp;
    add_header Cache-Control "max-age=86400, public";
}

Настройка заголовка уровня приложения


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


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


Node и express:


Добавьте global mount path:


app.use(function(req, res, next) {
    res.header('X-XSS-Protection', 1; mode=block);    
    next();
});

Java и Spring


У меня мало опыта работы со Spring, но у Baeldung есть отличное руководство по настройке заголовков в Spring.


PHP


Я не знаком с разнообразными средами PHP. Ищите промежуточное ПО для запросов. Для единичного запроса все просто.


header("X-XSS-Protection: 1; mode=block");

Python / Django


Django включает настраиваемое промежуточное ПО для обеспечения безопасности, которое выполнит за вас все эти настройки. Активируйте сначала их.


Ответы некоторых страниц можно трактовать как словарь. В Django есть особый способ работы с кэшированием, и если хотите настроить заголовки кэширования таким образом, с ним надо ознакомиться.


response = HttpResponse()
response["X-XSS-Protection"] = "1; mode=block"

Выводы


Настройка заголовков — процесс относительно простой и быстрый, зато дает прирост уровня безопасности сайта — в плане защиты данных, от межсайтового скриптинга и кликджекинга.


В будущем вы оградите себя от срыва сделок, потому что ваш рейтинг безопасности останется на уровне. Практика его оценки на основе рассмотренных мной параметров набирает обороты, и мне кажется, ее роль в ближайшие годы в сфере продаж только усилится.


Дайте знать, если я упустил какой-то важный заголовок!

Комментарии (4)


  1. jonie
    22.10.2019 15:01

    Всегда нужно выставлять вот такие флаги:
    Secure
    HTTPOnly

    Классическая (и рабочая везде) защита от XSRF основана как раз на чтении движком скриптовым cookie, но вы это сломаете. Подобная модель, например, используется в angular, в js либе axios, да и почти всюду (в довесок к новомодным хедерам)…


  1. UnclShura
    22.10.2019 15:33

    Access-Control-Allow-Origin похож на какое-то магическое заклинание, котороое на самом деле не защищает ни от чего. Что мешает свормировать HTTP запрос с «правильным» Origin?


    1. jonie
      22.10.2019 15:39
      +3

      это о браузере речь — браузер перед тем как сделать запрос (речь о js через fetch/XMLHttpRequest, не о, например img теге) с домена ХХХ на домен YYY сделает OPTIONS запрос на YYY и, если не увидит в ответе Access-Control-Allow-Origin с доменом ХХХ (ну или звездочку), то и запрос реальный делать не станет.

      Руками-то собрать любой HTTP запрос и дёрнуть его не в браузере (curl например) труда не составляет, тут все упирается в браузер и его безопасность.

      Например: Защищает стало быть от угона обычно токенов безопасности (того же httpOnly cookie) на чужой домен, когда сайтец выломали (на клиенте) и пытаются сделать из браузера запрос на свой домен. Проще говоря это позволяет установить доверительные отношения между от «домена-сайта» до «домена api».


  1. chelsea2
    23.10.2019 10:05
    -1

    Лучше пойти убится, чем писать такие статьи. Заголовок не отображает содержимого. Обзор 3х HTTP Headers высосан из пальца, а такое громкое название: «Полное руководство ..».