OCSP stapling in Nginx >=1.3.7
Maxim Dounin
mdounin на mdounin.ru
Вс Сен 16 23:36:54 UTC 2018
Hello!
On Sat, Sep 15, 2018 at 02:35:35PM +0300, Gena Makhomed wrote:
> On 12.09.2018 17:36, Maxim Dounin wrote:
>
> >>>>>>> Лучше всего - сделать так, чтобы OpenSSL научился проверять
> >>>>>>> OCSP-ответы не полной цепочкой сертификатов вплоть до доверенного
> >>>>>>> root'а, а ровно так, как и должно быть по стандарту - с помощью
> >>>>>>> одного только сертификата issuer'а. Тогда проблема исчезнет.
>
> Задал вопрос по этому поводу в списке рассылки openssl-users:
> https://mta.openssl.org/pipermail/openssl-users/2018-September/008795.html
> - пока что мне там никто ничего так и не ответил.
>
> >>> В зависимости от того, каким сертификатом подписан OCSP-ответ -
> >>> этого может быть достаточно или нет. RFC 6960 допускает два
> >>> варианта подписи в OCSP-ответах:
>
> >>> - issuer подписывает OCSP-ответ сам; или
> >>> - issuer выпускает сертификат, которым подписываются OCSP-ответы
> >>> (и этот сертификат включается в OCSP-ответ).
>
> >>> В обоих случаях для проверки OCSP-ответа достаточно знать
> >>> issuer'а, однако второй случай в OpenSSL не работает (впрочем, я
> >>> давно не порверял; но если верить "документации", в этом месте
> >>> ничего не зименилось). При этом по понятным причинам мало кто
> >>> использует собственно CA для подписи OCSP-ответов, соответственно
> >>> не работает это приблизительно всегда.
>
> Проверил второй случай, openssl-1.0.2k из CentOS 7.5 все работает:
>
> 1. Получаю сертификат сервера (0) и сертификат issuer'а (1):
> openssl s_client -showcerts -connect www.godaddy.com:443 < /dev/null | less
>
> Сертификат сервера сохраняю в файл www.godaddy.com-cert.pem
> а сертификат issuer'а - в файл www.godaddy.com-issuer.pem
>
> 2. Узнаю OCSP URI, это http://ocsp.godaddy.com/
> openssl x509 -text -noout -in www.godaddy.com-cert.pem | less
>
> 3. Делаю запрос на получение OCSP ответа от этого сервера:
> openssl ocsp -verify_other www.godaddy.com-issuer.pem -issuer
> www.godaddy.com-issuer.pem -cert www.godaddy.com-cert.pem -text -url
> http://ocsp.godaddy.com/ -header "Host" "ocsp.godaddy.com" | less
>
> Из ответа видно, что для подписи используется отдельный сертификат
> Go Daddy Validation Authority - G2, при этом "Response verify OK",
> то есть OpenSSL может проверить ответ сервера и в этом случае.
>
> Если убрать параметр '-verify_other www.godaddy.com-issuer.pem'
> тогда возвращается сообщение про ошибку:
>
> Response Verify Failure
> 140345419933584:error:27069065:OCSP
> routines:OCSP_basic_verify:certificate verify
> error:ocsp_vfy.c:138:Verify error:unable to get local issuer certificate
>
> Получается, что OpenSSL уже очень давно умеет проверять
> OCSP-ответ с помощью одного только сертификата issuer'а?
Получается, что в CentOS 7.5 есть bundle с доверенными корневыми
сертификатами, который используется openssl по умолчанию. Если
его отключить - указав параметры -CAfile и -CApath явно - то
валидация сломается.
> >>>> Насколько я понял из предыдущего обсуждения, нет разумных причин,
> >>>> чтобы выключать использование OCSP stapling и ocsp_stapling_verify.
> >>>>
> >>>> И вместе с тем, есть причины, чтобы их включать:
> >>>> OCSP stapling - сайт у клиентов будет открываться быстрее.
> >>>> ocsp_stapling_verify - не будет проблем с неразумными браузерами.
> >>
> >>> Про "быстрее" - есть нюансы. В частности, OCSP-ответ отправляется
> >>> клиенту, пытающемуся использовать OCSP stapling, в каждом
> >>> SSL handshake'е (а это местами может быть больно с точки зрения latency, не
> >>> говоря уже про трафик), в то время как в значительной части
> >>> случаев он клиенту на самом деле не нужен (например, уже есть в
> >>> кэше).
>
> Почему это может быть больно с точки зрения latency? Ведь nginx держит
> OCSP-ответ в своей памяти и отдает его клиенту практически мгновенно.
> Этот ответ как правило валиден от 24 часов до 7 или даже 14 дней.
>
> Трафик - это дополнительный килобайт, максимум два килобайта.
> Разве это много? Страница сайта весит от сотен килобайт
> до нескольких мегабайт, и 1-2 килобайта роли не играют.
Дополнительная пара килобайт в начале соедениния - это
потенциальный выход за пределы исходного окна, и соответственно
лишний RTT на соединение.
С получившим сейчас достаточно широкое распространение initcwnd10
(rfc6928, окно до 14k) - скорее всего плохо не будет (но тоже
вопрос, какая цепочка сертификатов при этом стоит), а если
используется более традиционный rfc3390 с окном до 4 килобайт -
как раз из-за stapling'а нарваться на лишний round-trip
элементарно.
Что до трафика, то всё зависит как раз от того, какой в данном
конкретном случае средний трафик передаётся по соединению. И
далеко не везде это сотни килобайт.
> Если OCSP-статус сертификата клиенту не нужен,
> зачем же в таком случае клиент его постоянно запрашивает?
>
> Это же клиент решает, получать ему OCSP-статус сертификата или нет.
Клиент не может знать, нужен ему статус или нет. Более того - он
даже не может знать, какой сертификат ему вернут. Соответственно
если он использует stapling - то в любом полном handshake'е
вынужден получать и stapling тоже.
[...]
> >>> Про verify - тоже есть нюансы. Дискуссия, если я её правильно
> >>> понял, как раз о том, что включать verify обходится дороже с
> >>> административной точки зрения, чем хотелось бы. При этом плюсы -
> >>> призрачны, ибо защищают от атак, которых на практике не бывает
> >>> (а если бы они были - браузеры бы давно исправились).
> >>
> >> В начале дискуссии вопрос был о том, почему для того чтобы директива
> >> ssl_stapling_verify on; нормально работала необходимо также прописывать
> >> ssl_trusted_certificate /etc/letsencrypt/live/example.com/chain.pem;
> >> если содержимое файла chain.pem можно получить из файла fullchain.pem
> >> выбросив оттуда первый сертификат, который является сертификатом сайта?
> >
> > И ответ на этот вопрос - потому что интерфейс проверки сертификатов
> > кривой, и требует массы дополнительных приседаний, чтобы работать.
> > Именно поэтому по умолчанию используется ssl_stapling_verify off.
>
> Передать библиотеке OpenSSL сертификат issuer'а два раза,
> в параметрах -issuer и -verify_other - это не выглядит очень сложным.
К сожалению, это работает не так. Точнее - так это не работает.
Всё что можно передать - nginx передаёт, но этого недостаточно,
если OCSP-ответы подписаны с использованием промежуточного
сертификата.
> > Кроме того, если у вас в ssl_certificate указана полная цепочка,
> > включая сертификат root CA, то это неправильно. Root-сертификат у
> > клиентов и так есть, отправлять клиентам его - бессмысленная трата
> > ресурсов, соответственно и включать его в цепочку в
> > ssl_certificate - не надо.
>
> У меня сертификат получен с помощью certbot,
> "Let’s Encrypt Authority X3" - это промежуточный сертификат.
> Он подписан с помощью "DST Root CA X3", подробности здесь:
> https://letsencrypt.org/certificates/
>
> В конфиге nginx прописано
>
> ssl_stapling on;
> ssl_stapling_verify on;
> resolver 127.0.0.1;
>
> ssl_certificate /etc/letsencrypt/live/.../fullchain.pem;
> ssl_certificate_key /etc/letsencrypt/live/.../privkey.pem;
>
> Директива ssl_trusted_certificate вообще нигде в конфиге не указана,
> при этом - nginx не пишет в лог никаких ошибок верификации сертификата.
Потому что letsencrypt - подписывает OCSP-ответы непосредственно
issuer'ом, не используя промежуточный сертфикат. Соответственно
для него - ssl_stapling_verify работает без дополнительных
приседаний, хватает сертифката issuer'а, который nginx достаёт из
цепочки ssl_certificate.
К сожалению, это далеко не всегда так. И к ещё большему сожалению -
никто не гарантирует, что в один прекрасный момент это не
перестанет быть так и для letsencrypt тоже.
[...]
> Кстати, пользователи жалуются, что есть BUG в nginx,
> связанный с сертификатами с флагом "OCSP Must Staple":
> https://blog.crashed.org/nginx-stapling-busted/
Потому что "Must Staple" - это попытка превратить OCSP stapling из
механизма оптимизации в обязательный механизм, аналогичный
короткоживущим сертификатам. Не сюрприз, что так не работает -
требования совершенно разные.
Любители Must Staple общаются в траке в двух тикетах:
https://trac.nginx.org/nginx/ticket/812
https://trac.nginx.org/nginx/ticket/990
Пока что они делают это с нулевым полезным выходом.
--
Maxim Dounin
http://mdounin.ru/
Подробная информация о списке рассылки nginx-ru