Реми ван Элст. HKPK - расширение для фиксации публичного ключа HTTP в Apache, nginx и Lighttpd, 2014

Перевод статьи: HTTP Public Key Pinning Extension HPKP for Apache, NGINX and Lighttpd

Автор: Реми ван Элст (Remy van Elst)

Фиксация публичного ключа означает, что цепочка сертификатов должна включать публичный ключ из разрешённого списка. Это даёт гарантии того, что только удостоверяющий центр из разрешённого списка может подписывать сертификаты для *.example.com, но не какой-то другой удостоверяющий центр, сертификат которого имеется в хранилище браузера. В этой статье объясняется теория и приводятся примеры настройки Apache, Lighttpd и nginx.

1. HPKP - Расширение для фиксации публичного ключа HTTP

Допустим, что имеется банк, который всегда использует сертификаты, выпущенные удостоверяющим центром компании A. При нынешней системе сертификации удостоверяющие центры компаний B, C и удостоверяющий центр АНБ могут создать сертификат этого банка, который будет приниматься, потому что эти компании также являются доверенными корневыми удостоверяющими центрами.

Если банк воспользуется расширением для фиксации публичного ключа - HPKP и зафиксирует первый промежуточный сертификат (от удостоверяющего центра компании A), браузеры не будут принимать сертификаты от удостоверяющих центров компаний B и C, даже если цепочка доверия верна. HPKP также позволяет браузеру отправлять в банк отчёты о попытках подмены, чтобы банк знал о проводящейся атаке.

Расширение для фиксации публичного ключа HTTP - Public Key Pinning Extension for HTTP (HPKP) - это стандарт пользовательских агентов HTTP, которые были разработаны начиная с 2011 года. Начало стандарту было положено в Google, когда фиксация была реализована в Chrome. Однако, впоследствии разработчики поняли, что ручное поддержание списка фиксированных сайтов не масштабируемо.

Вот краткий обзор возможностей HPKP:

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

2. Отпечаток SPKI - теория

Как объясняет в своей статье Адам Лэнгли (Adam Langley), хэшируется публичный ключ, а не сертификат:

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

Например, у StartSSL имеется два корневых сертификата: один подписан SHA1, а другой - SHA256. Если нужно зафиксировать StartSSL как удостоверяющий центр, то какой из сертификатов следует использовать? Нужно использовать оба, но как узнать что есть второй корень, если об этом не сообщается?

В то же время, для этой цели годятся хэши публичных ключей:

Браузеры считают, что сертификат веб-сервера неизменен: он всегда является началом цепочки. Сертификат сервера содержит подпись родительского сертификата, которая должна быть верной. Подразумевается, что публичный ключ родителя неизменен для дочернего сертификата. И так далее, вся цепочка публичных ключей оказывается неизменной.

Единственный тонкий момент заключается в том, что не стоит фиксировать кросс-сертифицированные корневые сертификаты. Например, корневой сертификат GoDaddy подписан сертификатом Valicert, так что старые клиенты, не знающие корневой сертификат GoDaddy всё-таки доверяют таким сертификатам. Однако, не стоит фиксировать сертификат Valicert, потому что более новые клиенты завершают цепочку сертификатом GoDaddy.

Также хэшируется SubjectPublicKeyInfo, но не двоичная последовательность публичного ключа. SPKI, наряду с самим публичным ключом, включает в себя тип публичного ключа и некоторые параметры. Это важно, потому что простое хэширование публичного ключа оставляет возможность для атаки из-за неправильной интерпретации. Рассмотрим публичный ключ Диффи-Хеллмана: если хэшируется только публичный ключ, а не SPKI полностью, то атакующий может воспользоваться тем же публичным ключом, но заставить клиента интерпретировать его как другую группу Диффи-Хеллмана. Таким же образом можно заставить браузер интерпретировать ключ RSA как ключ DSA и т.п.

3. Что фиксировать

Что нужно фиксировать? Не стоит фиксировать собственный публичный ключ. Ключ может смениться или оказаться скомпрометированным. Может понадобиться использовать несколько сертификатов, а ключ может смениться из-за слишком частой ротации сертификатов. Ключ может оказаться скомпрометированным, если веб-сервер взломают.

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

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

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

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

Альтернативный и более безопасный способ решения этой проблемы - создать заблаговременно по меньшей мере три разных публичных ключа (с помощью OpenSSL, обратитесь к странице с Javascript, которая генерирует команду OpenSSL для генерации) и хранить два из этих ключей как резерв в безопасном месте, вне сети и в другом помещении.

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

Это не проблема для HPKP, поскольку хэши берутся от SPKI публичного ключа, а не сертификата. Просроченная или отличающаяся цепочка удостоверяющего центра, подписавшего сертификат, в этом случае не имеет значения.

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

4. Отпечаток SPKI

Для получения отпечатка SPKI из сертификата можно воспользоваться командой OpenSSL, которая указана в черновике RFC:

openssl x509 -noout -in certificate.pem -pubkey | \
openssl asn1parse -noout -inform pem -out public.key;
openssl dgst -sha256 -binary public.key | openssl enc -base64

Результат:

klO23nT2ehFDXCfx3eHTDRESMz3asj1muO+4aIdjiuY=

Файл certificate.pem, подаваемый на вход команды - это первый сертификат в цепочке для этого веб-сайта. На момент написания - сервер удостоверяющего центра для безопасной проверки доменов COMODO RSA (COMODO RSA Domain Validation Secure Server CA), серийный номер - 2B:2E:6E:EA:D9:75:36:6C:14:8A:6E:DB:A3:7C:8C:07.

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

5. Неполадки

На момент написания этой статьи (январь 2015 года) HPKP поддерживает только один браузер (Chrome), и у него имеется важная проблема, которая заключается в том, что Chrome понимает директивы max-age и includeSubdomains из заголовков HSTS и HPKP как взаимно исключающие. Это означает, что если имеется HSTS и HPKP с разными политиками max-age или includeSubdomains, они будут перетасовываться. Обратитесь к описанию этой неполадки за более подробной информацией: HPKP принудительно использует includeSubDomains даже когда директива не указана. Благодарю Скотта Хелми (Scott Helme) из https://scotthelme.co.uk за исследования и за то, что сообщил о проблеме мне и в проект Chromium.

6. Настройка веб-сервера

Далее приведены инструкции по настройке трёх наиболее популярных веб-серверов. Поскольку HPKP - это просто заголовок HTTP, большинство веб-серверов позволяют задать его. Заголовок нужно настраивать на веб-сайте HTTPS.

Ниже указан пример фиксации сервера удостоверяющего центра для безопасной проверки доменов COMODO RSA (COMODO RSA Domain Validation Secure Server CA) и запасного удостоверяющего центра 2 Comodo PositiveSSL (Comodo PositiveSSL CA 2), со сроком годности в 30 дней, включая все поддомены.

6.1. Apache

Отредактируйте файл конфигурации Apache (например, /etc/apache2/sites-enabled/website.conf или /etc/apache2/httpd.conf) и добавьте следующие строки в секцию VirtualHost:

# Загрузка не обязательного модуля headers:
LoadModule headers_module modules/mod_headers.so

Header set Public-Key-Pins "pin-sha256=\"klO23nT2ehFDXCfx3eHTDRESMz3asj1muO+4aIdjiuY=\"; pin-sha256=\"633lt352PKRXbOwf4xSEa1M517scpD3l5f79xMD9r9Q=\"; max-age=2592000; includeSubDomains"

6.2. Lighttpd

В случае Lighttpd всё очень просто. Добавим в файл конфигурации Lighttpd (например, в /etc/lighttpd/lighttpd.conf) следующие строки:

server.modules += ( "mod_setenv" )
$HTTP["scheme"] == "https" {
  setenv.add-response-header = ( "Public-Key-Pins" => "pin-sha256=\"klO23nT2ehFDXCfx3eHTDRESMz3asj1muO+4aIdjiuY=\"; pin-sha256=\"633lt352PKRXbOwf4xSEa1M517scpD3l5f79xMD9r9Q=\"; max-age=2592000; includeSubDomains")
}

6.3. nginx

Настройка nginx даже ещё короче. Добавьте следующую строку в блок server, относящийся к настройке HTTPS:

add_header Public-Key-Pins 'pin-sha256="klO23nT2ehFDXCfx3eHTDRESMz3asj1muO+4aIdjiuY="; pin-sha256="633lt352PKRXbOwf4xSEa1M517scpD3l5f79xMD9r9Q="; max-age=2592000; includeSubDomains';

7. Отчёты

Отчёты HPKP позволяют пользовательским агентам отправлять вам отчёты о проблемах.

Если добавить к заголовку дополнительный параметр report-uri="http://example.org/hpkp-report" и настроить по этой ссылке прослушиватель, клиенты будут отправлять отчёты при возникновении проблем. Отчёт отправляет JSON-данные на адрес из report-uri в теле POST-запроса:

{
    "date-time": "2014-12-26T11:52:10Z",
    "hostname": "www.example.org",
    "port": 443,
    "effective-expiration-date": "2014-12-31T12:59:59",
    "include-subdomains": true,
    "served-certificate-chain": [
        "-----BEGINCERTIFICATE-----\nMIIAuyg[...]tqU0CkVDNx\n-----ENDCERTIFICATE-----"
    ],
    "validated-certificate-chain": [
        "-----BEGINCERTIFICATE-----\nEBDCCygAwIBA[...]PX4WecNx\n-----ENDCERTIFICATE-----"
    ],
    "known-pins": [
        "pin-sha256=\"dUezRu9zOECb901Md727xWltNsj0e6qzGk\"",
        "pin-sha256=\"E9CqVKB9+xZ9INDbd+2eRQozqbQ2yXLYc\""
    ]
}

8. Не фиксировать, только отчитываться

С помощью заголовка Public-Key-Pins-Report-Only можно настроить HPKP в режиме отправки отчётов без принуждения соблюдать фиксацию.

Этот подход позволяет настроить фиксацию так, что сайт не станет недостижимым, если HPKP настроен неправильно. Позже можно включить принудительное использование фиксации заменой заголовка обратно на Public-Key-Pins.

Написать автору перевода