HPKP (Key Pinning) на dxdt.ru

HPKPПолучается непрерывная серия записок про TLS и связанные технологии, но, тем не менее, продолжу. Добавил на dxdt.ru поддержку технологии под названием HPKP – HTTP Public Key Pinning. Key Pinning – это привязывание публичного ключа к сетевому ресурсу (по имени или по адресу) на стороне клиента. Принцип хорошо известен из практики использования SSH: здесь отпечатки, идентифицирующие серверы, при первом контакте сохраняются на клиенте; в дальнейшем, изменение отпечатка позволит обнаружить возможную подмену ключей в рамках атаки “человек посередине”. Для HTTP предложена технология (RFC 7469), базирующаяся на той же идее.

Веб-сервер передаёт специальное поле в составе заголовков HTTP. Поле содержит отпечатки (значение хеш-функции) публичных ключей, которые используются данным сервером при установлении TLS-соединения, а также время, в течение которого клиенту следует помнить отпечатки. Браузер (то есть, клиент HTTPS), впервые обнаружив отпечатки ключей, запоминает их. При следующих обращениях к данному ресурсу – отпечатки сверяются с переданными ключами: если обнаружено расхождение, то браузер не устанавливает соединение и выдаёт предупреждение системы безопасности. HPKP поддерживается, например, в Chrome и Firefox.

Метод позволяет обнаружить подмену ключей, например, в схеме с выпуском “перехватывающего” сертификата для заданного доменного имени. Дело в том, что узел, перехватывающий TLS, даже если у него имеется валидный сертификат для атакуемого домена, обычно не имеет оригинального секретного серверного ключа – вместо него он использует свой. Значение отпечатка у этого перехватывающего ключа другое, поэтому браузер сможет обнаружить подмену. Если HPKP не использовать, то перехват пройдёт незамеченным, так как цепочка сертификатов будет валидной.

Посмотрим на реализацию подробнее, на примере dxdt.ru:

$ wget -S -O /dev/null https://dxdt.ru/ 2>&1 | grep ‘Public-Key-Pins:’ | tr -s ‘ ‘ | sed ‘s/\;/\;\n/g’

Public-Key-Pins: pin-sha256=”WXeGHAmPHRPwX7CbyIUCQm9mtStbVAZR2LOvTAAQKNg=”;
pin-sha256=”dj2qz8mxs8NSpJdISrUPRfL86ZGu3QNkJm4hL+opE0o=”;
pin-sha256=”nq9/zIb/QKb+tHd1ZU5keAYkYzrA8zx7tMmVtDDNG0M=”;
pin-sha256=”kkwURRddFrj4gRH7ILMp/Fqrvl6kfO4o2ekBOmiP6B0=”;
max-age=270127;

Поле HPKP называется Public-Key-Pins и, в нашем примере, содержит четыре значения pin-sha256 – это и есть отпечатки ключей (значение SHA-256 от публичного ключа, взятое в Base64). Почему их четыре? Потому что на dxdt.ru поддерживается два типа подписей: ECDSA и RSA, соответственно, нужно публиковать отпечатки для каждого из них; кроме того, стандарт требует указания как минимум одного “запасного ключа”, который отсутствует в цепочке валидации – я решил опубликовать по запасному ключу для каждого типа подписи. Итого: четыре отпечатка. Дополнительные отпечатки, очевидно, требуются для того, чтобы можно было поменять ключи без потери доступности сайта.

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

Параметр max-age – определяет (в секундах) время, в течение которого браузер должен сохранять отпечатки. Я, пока что, установил довольно осторожное значение: около трёх суток.

Кроме того, поле Public-Key-Pins позволяет использовать ещё пару параметров: includeSubdomains – это флаг, распространяющий действие отпечатков на поддомены; report-uri – адрес, на который браузер будет передавать сообщения об ошибках. Последний параметр особенно интересен. Он позволяет поднять сервер, который будет получать сообщения от клиентских браузеров, которые натолкнулись на ошибки в работе HPKP – это позволяет обнаруживать атаки “человек посередине”, что называется, в дикой природе. Сообщение содержит детали контекста, в котором возникла ошибка. Эту функцию уже реализует Google Chrome. Естественно, для приёма сообщений нужен отдельный домен. Создание такой точки приёма для dxdt.ru – следующий шаг: нужно выделить сервер и запрограммировать обработчик сообщений.

Addon:

Как получить отпечатки ключей? Достаточно просто, если воспользоваться OpenSSL (я использовал файлы секретных ключей).

Для RSA:

openssl rsa -pubout -in rsa-private.pem -outform DER | openssl dgst -sha256 -binary | openssl enc -base64

Для ECDSA:

openssl ec -pubout -in ec-private.pem -outform DER | openssl dgst -sha256 -binary | openssl enc -base64

Адрес записки: https://dxdt.ru/2015/12/08/7759/

Похожие записки:



Далее - мнения и дискуссии

(Сообщения ниже добавляются читателями сайта, через форму, расположенную в конце страницы.)

Комментарии читателей блога: 29

  • 1. 9th December 2015, 10:56 // Читатель sarin написал:

    а можно так-же, но чтобы не передавать отпечатки в хедере, а клиент сам вычислил отпечаток при первом соединении и запомнил?

  • 2. 9th December 2015, 11:57 // Читатель Kunis написал:

    Кстати, передача отпечатков в отдельном поле немного усложняет жизнь перехватчику, если он сидит посередине с самого начала. Ему еще надо озаботиться сканированием HTTP заголовков и подмену отпечатков там. Если перехватчик этого не сделает, то после первого же сеанса, клиент откажется с ним разговаривать.

  • 3. 9th December 2015, 17:00 // Александр Венедюхин:

    > а можно так-же, но чтобы не передавать отпечатки в хедере,
    > а клиент сам вычислил отпечаток при первом соединении и запомнил?

    Можно. Есть такие расширения для браузеров. Например, для Firefox: Certificate Patrol https://addons.mozilla.org/en-US/firefox/addon/certificate-patrol/ (не уверен, что всё ещё поддерживается). Штатно браузеры такое не делают, потому что будет много проблем и ложных срабатываний: сама по себе замена ключа сервера или сертификатов в TLS не является чем-то необычным.

  • 4. 9th December 2015, 17:01 // Александр Венедюхин:

    > Ему еще надо озаботиться сканированием HTTP заголовков и подмену отпечатков там.

    Да, именно так. Единственное, что можно не подменять отпечатки, а просто вырезать это поле (в случае с HTTPS, правда, для этого потребуется перехват соединения).

  • 5. 11th August 2016, 02:15 // Читатель Сергей Виноградов написал:

    Какой смысл в HPKP, если срок устаревания всего 3 дня?

  • 6. 11th August 2016, 11:07 // Александр Венедюхин:

    Пока что я считаю, что HPKP в стадии тестирования – отсюда и такой короткий срок. Если всё продолжит работать без проблем, то срок устаревания увеличу.

  • 7. 15th August 2016, 22:49 // Читатель Сергей Виноградов написал:

    Интересно, а три лишних сертификата в hpkp – это следующие на очереди серверные сертификаты или что?

  • 8. 16th August 2016, 00:19 // Александр Венедюхин:

    Про какие сертификаты речь? В HPKP сертификатов нет.

  • 9. 16th August 2016, 13:42 // Читатель Сергей Виноградов написал:

    > Про какие сертификаты речь?

    pin-sha256

  • 10. 16th August 2016, 13:46 // Читатель Сергей Виноградов написал:

    Кстати, вы не знаете, в чём может быть дело.
    Когда захожу на https://dxdt.ru/feed/ из FireFox – работает.
    Когда пытаюсь подписаться из FeedReader (инсталлируемый rss-клиент) – впечатление, что не может соединится. На моей стороне, вроде бы, нет никаких блокировок.

  • 11. 16th August 2016, 14:29 // Александр Венедюхин:

    > pin-sha256

    Это отпечатки ключей. Вроде, их количество объясняется в исходной записке:

    Почему их четыре? Потому что на dxdt.ru поддерживается два типа подписей: ECDSA и RSA, соответственно, нужно публиковать отпечатки для каждого из них; кроме того, стандарт требует указания как минимум одного “запасного ключа”, который отсутствует в цепочке валидации – я решил опубликовать по запасному ключу для каждого типа подписи. Итого: четыре отпечатка.

    Ну, то есть, сервер поддерживает и ECDSA, и RSA. Серверные ключи для этих криптосистем – разные. Они не могут быть одинаковыми. Сертификаты, соответственно, – тоже: для ECDSA сертификат от Comodo, для RSA – от WoSign. Так что сертификаты не лишние. Другое дело, что их состав можно было бы варьировать в зависимости от устанавливаемого соединения, но оказалось, что mod_ssl так не умеет, поэтому выдаются все. Ну и pin-ы тоже для всех ключей: два действующих, два запасных.

  • 12. 16th August 2016, 14:40 // Александр Венедюхин:

    > Когда пытаюсь подписаться из FeedReader (инсталлируемый rss-клиент) – впечатление, что не может соединится.

    Тут, к сожалению, ничего не могу сказать определённо. Могу предположить, что этот клиент использует какую-нибудь старую библиотеку для TLS, которая не способна установить соединение с сервером, так как не поддерживает нужные параметры TLS.

  • 13. 16th August 2016, 14:54 // Читатель Сергей Виноградов написал:

    > Потому что на dxdt.ru поддерживается два типа подписей: ECDSA и RSA

    Признаться, я не очень понял этого момента. Идёт переключение выдаваемого сертификата в зависимости от полученного chipher-suit?

  • 14. 16th August 2016, 15:05 // Александр Венедюхин:

    Переключения как раз нет: сертификаты передаются все сразу. То же самое касается и pin-ов: все сразу передаются.

  • 15. 16th August 2016, 15:20 // Читатель Сергей Виноградов написал:

    А какой тогда в этом смысл, поддерживать две версии сертификатов, передавая все одновременно?

  • 16. 16th August 2016, 15:22 // Александр Венедюхин:

    Смысл в том, чтобы была поддержка и ECDSA, и RSA – чтобы клиенты, которые не умеют ECDSA, всё равно могли подключиться.

  • 17. 16th August 2016, 15:56 // Читатель Сергей Виноградов написал:

    Смысл в поддержке ECDSA какой?

    Ведь если украдут RSA, то в ECDSA смысла нет. Ну только если в смысле того, что ECDSA быстрее считается.

  • 18. 16th August 2016, 18:58 // Александр Венедюхин:

    ECDSA – современный вариант, при наличии ECDSA можно весь набор сделать “эллиптическим”, приплюсовав ECDHE (Диффи-Хеллмана), но нужен специальный сертификат, то есть, RSA-сертификат – не годится. Соответственно, ECDSA – основной вариант, современный, быстрый и компактный. RSA – оставлен для совместимости.

  • 19. 28th August 2016, 02:23 // Читатель Сергей Виноградов написал:

    Кстати, а это нормально, что HPKP-заголовки не отдаются на всех запросах?
    Например, этот не отдаёт
    https://dxdt.ru/wp-content/2016/08/pmpkn701.jpg

  • 20. 28th August 2016, 12:43 // Александр Венедюхин:

    Нет, не нормально – HPKP отдаётся сервером на всех запросах (я проверил настройки: в том числе, для указанного документа – там тоже есть HPKP, по крайней мере, я его вижу).

  • 21. 28th August 2016, 17:07 // Читатель Сергей Виноградов написал:

    Да, действительно, всё в порядке.
    Это у меня ошибка была. Спасибо :)

  • 22. 31st August 2016, 02:41 // Читатель Сергей Виноградов написал:

    Забавно. Если сервер отдаёт “HTTP/1.1 304 Not Modified”, то HPKP-заголовка, всё-таки, нет.

  • 23. 31st August 2016, 22:11 // Александр Венедюхин:

    Это потому, что спецификация не разрешает дополнительные поля при ответе с кодом 304.

  • 24. 30th September 2016, 13:36 // Читатель xM написал:

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

  • 25. 30th September 2016, 13:59 // Александр Венедюхин:

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

    Другими словами: требуется, чтобы хотя бы один пин совпал.

  • 26. 30th September 2016, 14:50 // Читатель xM написал:

    Да, совпадение и чтобы их было, как минимум два. Причём один из них не был привязан к применяемой цепочке сертификатов.
    Спасибо.

  • 27. 30th September 2016, 15:13 // Читатель xM написал:

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

  • 28. 30th September 2016, 18:49 // Александр Венедюхин:

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

  • 29. 30th September 2016, 21:40 // Читатель xM написал:

    Ещё раз спасибо. Схема автоматического обновления сертификатов Let’s Encrypt и их пришпиливания примерно нарисовалась.