28 Октябрь 2016

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

HTTP headers and web server security

В опубликованных ранее постах уже затрагивался вопрос обеспечения должного уровня безопасности интернет сервисов посредством множества доступных в настоящее время механизмов, таких, например, как сертификаты SSL / TLS или электронную подпись DKIM.

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

1. Добавление заголовков HTTP

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

Рассмотрим наиболее полезные из них на практике.

1.1. HTTP Strict Transport Security

HTTP Strict Transport Security или HSTS уже упоминался в статье "Усиливаем HTTPS на web-сервере", но без объяснения назначения и механизма работы.

Суть его состоит в форсировании установления защищённого протоколами SSL / TLS соединения на определённый интервал времени в том случае, если такое соединение удалось хотя бы один раз успешно установить ранее.

Это обеспечивается путём указания необходимых параметров в заголовке Strict-Transport-Security. Стандартное его значение следующее (здесь и далее даётся нотация формата конфигурационного файла веб-сервера Lighttpd):

root@beta:/home/xm # cat /usr/local/etc/lighttpd/lighttpd.conf
...
var.strict-transport-security = (
        "Strict-Transport-Security" => "max-age=15768000"
)
...

Здесь устанавливается max-age время в секундах, в течение которого сайт должен будет отвечать по защищённому протоколу. В противном случае соединение вообще не будет установлено и появится сообщение об ошибке.

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

Третьим параметром, который дополнительно повышает действенность HSTS является preload. Он активирует использование браузером специальной базы данных сайтов, где сайты опубликовавшие этот параметр уже фигурируют как требующие использование SSL / TLS. Таким образом, попав в неё сайту на протяжении указанного времени уже даже нет необходимости вновь передавать клиенту заголовок HSTS — он уже получит эти сведения из этой базы данных. Таким образом политика оказывается, как бы, предзагруженной в браузер вне зависимости от полученного ответа от сервера.

Один из авторов браузера Chrome Adam Langley создал специальный сайт, который позволяет добавить ваш сайт в эту базу данных в ручном режиме. К сожалению, на момент написания данной статьи он не поддерживает механизм SNI, поэтому добавить получится только те сайты, которые используют либо выделенный IP адрес, либо используют корневой сертификат для IP адреса.

В настоящее время этот механизм поддерживается браузерами Chrome, Firefox, Opera, Safari, IE 11 and Edge.

Подробности о HSTS см. в RFC6797, а с примером имплементации SNI на хосте можно ознакомиться в статье "Использование нескольких SSL сертификатов на одном IP адресе".

1.2. HTTP Public Key Pinning

HTTP Public Key Pinning или HPKP достаточно подробно был освещён в статье "Технология HTTP Public Key Pining: внедрение и автоматизация совместно с Let's Encrypt", поэтому подробно останавливаться на нём здесь смысла нет.

Напомню лишь, что данная технология реализуется через цифровой отпечаток TLS сертификата посредством передачи его и сопутствующих параметров через заголовок Public-Key-Pins (или Public-Key-Pins-Report-Only для нужд тестирования).

Подробности о HPKP см. в RFC7469.

1.3. Content Security Policy

Content Security Policy или CSP обеспечивает защиту от целого набора атак — межсайтового скриптинга (XSS), кликджэкинга (clickjacking) и внедрения кода. Подробно останавливаться на этих атаках в рамках данной статьи не имеет смысла, поскольку это довольно обширная и интересная тема. Скажу лишь, что все они могут быть весьма неприятны и опасны для посетителей сайта, равно как и для его владельцев.

Суть данного метода состоит в передаче клиенту через заголовок Content-Security-Policy набора правил согласно которому сайт будет обращаться к контенту. При этом данные правила могут различаться по типам скачиваемых данных, используемых объектов и механизмов исполнения, а также источникам и даже содержимому. Например, можно ограничить источники загрузки CSS, изображений, внешних шрифтов, скриптов и т.п., разрешить запуск определённым образом подписанного скрипта или скрипта имеющего конкретную контрольную сумму.

Например, добавив для этого сайта следующий заголовок, мы ограничим загрузку всех ресурсов только с начинающихся на kostikov.co:

var.strict-transport-security += (
        "Content-Security-Policy" => "default-src: 'self'"
)

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

На практике, разумеется, такое жёсткое ограничение вряд ли можно будет использовать, поскольку оно, к примеру, заблокирует работу счётчиков и системы анализа Google или загрузку изображений из внешних источников. Поэтому в реальности для сайта kostikov.co работающего под управлением CMS Bludit используется существенно более сложная политика. Она учитывает использование всех внешних источников ресурсов для полноценной работы сайта в настоящее время не оставляя, при этом, места для внедрения потенциальным злоумышленником иных нужных ему целей:

root@beta:/home/xm # cat /usr/local/etc/lighttpd/lighttpd.conf
...
var.strict-transport-security += (
        "Content-Security-Policy" => "upgrade-insecure-requests; default-src 'self'; script-src 'unsafe-inline' 'unsafe-eval' https://www.google-analytics.com https://mc.yandex.ru https://static.addtoany.com https://*.disqus.com https://*.disquscdn.com 'self'; style-src 'unsafe-inline' https://fonts.googleapis.com https://static.addtoany.com https://*.disquscdn.com 'self'; font-src https://fonts.gstatic.com 'self'; connect-src https://mc.yandex.ru https://links.services.disqus.com 'self'; img-src https://www.google-analytics.com https://mc.yandex.ru https://static.addtoany.com data: https://*.disquscdn.com https://referrer.disqus.com https://affiliates.purevpn.com 'self'; frame-src https://static.addtoany.com https://disqus.com 'self'"
)
...

Как видно, здесь предоставлена возможность загрузки внешних шрифтов, систем анализа Google и Yandex, системы добавления ссылок AddToAny и комментирования Disqus.

Обратите внимание, что здесь используются специальные ключевые слова. upgrade-insecure-requests заставляет всегда использовать защищённое соединение вне зависимости от указанных в URL, unsafe-inline разрешает встраивание кода непостредственно в свойства HTML тэгов, а unsafe-eval включает механизмы типа eval(). Также существует ещё один тип none, который вообще запрещает загрузку ресурса указанного типа откуда бы то нибыло.

Кроме того, допускаются шаблона замены в виде звёздочки в различых местах URL, к примеру

var.strict-transport-security += (
        "Content-Security-Policy" => "default-src: *://*.kostikov.co:*"
)

разрешит доступ к ресурсам для всех поддоменов kostikov.co (но не для него самого) по протоколам http, https, через их указание в качестве порта после двоеточия, равно как и для любого другого порта.

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

Описание изображения

Подобно используемому в HTTP Public Key Pinning методу имеется также и возможность отправки JSON-отчётов во внешний обработчик через передачу параметров в директиве report-uri.

Стандарт Content Security Policy достаточно обширен и имеет богатый набор возможностей, по этому см. подробное его описание в черновике W3C.

1.4. X-Frame-Options

X-Frame-Options и одноимённый заголовок также предназначен для защиты от кликджэкинга, который можно осуществить путём вызова вашего сайта через HTML тэг iframe. За деталями реализации прошу пройти по ссылке.

В качестве значения передаваемого в заголовке используется три типа команд: ALLOW-FROM разрешает встраивание для указанного URL, SAMEORIGIN разрешает его только для самого сайта, а DENY вовсе запрещает встраивание во фрейм.

Разумной и наиболее безопасной настройкой для стандартного сайта будет полный запрет.

root@beta:/home/xm # cat /usr/local/etc/lighttpd/lighttpd.conf
...
var.strict-transport-security += (
        "X-Frame-Options" => "DENY"
)
...

Подробности см. в RFC7034.

1.5. X-Xss-Protection

Заголовок X-Xss-Protection, как очевидно из названия, направлен на защиту от межсайтового скриптинга, а точнее такого его вида как "отражённый XSS" (он же "XSS тип 1").

Он имеет всего два параметра, которые сообщают браузеру об ожидаемом поведении.

root@beta:/home/xm # cat /usr/local/etc/lighttpd/lighttpd.conf
...
var.strict-transport-security += (
        "X-XSS-Protection" => "1; mode=block"
)
...

Значение 1 включает механизм защиты от подобных атак, а mode=block запрещает обработку страниц, где они замечены.

1.6. X-Content-Type-Options

X-Content-Type-Options имеет всего одно значение параметра nosniff, которое запрещает браузеру проводить автоматическое определение типа передаваемого контента MIME заставляя использовать ровно тот, который был указан в заголовке Content-Type. Это не позволяет злоумышленнику подставить содержимое другого типа, нежели было анонсировано при передаче использовав неисполняемый типа данных для передаче исполняемого.

root@beta:/home/xm # cat /usr/local/etc/lighttpd/lighttpd.conf
...
var.strict-transport-security += (
        "X-Content-Type-Options" => "nosniff"
)
...

Подробнее см. по ссылке.

2. Удаление заголовков HTTP

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

2.1. Имя сервера

По стандарту HTTP, который описан в RFC2616, в каждом передаваемом с сервера заголовке содержится информация о версии программного обеспечения, которая используется в работе. Она содержится в поле Server.

Например для этого сайта стандартный ответ будет таким.

root@beta:/home/xm # curl -I https://kostikov.co
HTTP/1.1 200 OK
Content-Security-Policy: upgrade-insecure-requests; default-src 'self'; script-src 'unsafe-inline' 'unsafe-eval' https://www.google-analytics.com https://mc.yandex.ru https://static.addtoany.com https://*.disqus.com https://*.disquscdn.com 'self'; style-src 'unsafe-inline' https://fonts.googleapis.com https://static.addtoany.com https://*.disquscdn.com 'self'; font-src https://fonts.gstatic.com 'self'; connect-src https://mc.yandex.ru https://links.services.disqus.com 'self'; img-src https://www.google-analytics.com https://mc.yandex.ru https://static.addtoany.com data: https://*.disquscdn.com https://referrer.disqus.com https://affiliates.purevpn.com 'self'; frame-src https://static.addtoany.com https://disqus.com 'self'
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
X-XSS-Protection: 1; mode=block
Strict-Transport-Security: max-age=15768000
Public-Key-Pins: pin-sha256="/Zzd6uB5QqnA/H7iu/X4cFjO5eY2zZ/4cJt32L4MQQQ="; pin-sha256="WE6nwK3QjTqBzMJibxXUD2MP0OtmQEU+EbyQDlU+7w8="; max-age=5184000
X-Powered-By: PHP/5.6.27
Set-Cookie: Bludit-KEY=0cdd8da056935e02094174b7ff51df9e; path=/; HttpOnly
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
Pragma: no-cache
Content-type: text/html; charset=UTF-8
Date: Fri, 28 Oct 2016 20:46:48 GMT
Server: lighttpd/1.4.42

Как видно, он честно сообщает, что используется Lighttpd версии 1.4.42. Чтобы злоумышленник не смог узнать эту информацию и воспользоваться целенаправленным поиском потенциальной проблемы в безопаности именно для Lighttpd, можно подменить данный заголовок на произвольный через параметр конфигурации server.tag.

root@beta:/home/xm # cat /usr/local/etc/lighttpd/lighttpd.conf | grep server.tag
server.tag = "don't ask, don't tell"

Таким образом клиент увидит вместо версии смущающую строку "don't ask, don't tell".

2.2. X-Powered-By

В приведённом выше ответе веб-сервера в заголовке можно заметить также поле X-Powered-By, которое анонсирует используемую для данного сайте технологию. В данном случае анонсирован PHP версии 5.6.27.

Удалив или заменив эту информацию на нечто иное мы также можем осложнить жизнь злонамеренному посетителю сайта. Для PHP проще всего это сделать через файл его конфигурации php.ini установив параметр expose_php в выключенное состояние.

root@beta:/home/xm # cat /usr/local/etc/php.ini | grep expose_php
expose_php = off

3. Проверка результата

Для тестирования корректности сделанных настроек и влияние результата их выполнения на безопасность сайта рекомендую воспользоваться замечательным ресурсом https://securityheaders.io.

Описание изображения

Надеюсь, что благодаря вышеизложенной информации ваш результат не будет хуже моего!

4. PROFIT!

Статья была полезной? Тогда прошу не стесняться и поддерживать деньгами через PayPal или Яндекс.Деньги.


bludit  lighttpd  security  SSL  HTTP