HPKP (Key Pinning) на dxdt.ru
Получается непрерывная серия записок про 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/
Похожие записки:
- Реплика: перенос доменных имён и GoDaddy
- Экспериментальный сервер TLS 1.3: обновление
- DNS как база данных
- Описание DoS-атаки с HTTP/2 от Cloudflare
- Вывод полей ECH на tls13.1d.pw
- TLS 1.3 в Рунете
- Полностью зашифрованные протоколы и DPI-блокирование
- Перспективный ИИ в "разработке кода"
- Системы счисления и системное администрирование
- Twitter за стеной регистрации
- Имена в TLS для веба (HTTP/HTTPS)
Комментарии читателей блога: 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 и их пришпиливания примерно нарисовалась.