it-roy-ru.com

Почему Ruby не может проверить сертификат SSL?

Я впервые пытаюсь использовать библиотеку XMLRPC :: Client для взаимодействия с удаленным API, и я продолжаю получать эту ошибку:

warning: peer certificate won't be verified in this SSL session

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

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

OpenSSL:SSL::SSLError:
  SSL_connect returned=1 errno=0 state=SSLv3 read server certificate B:
  certificate verify failed

Конечно! Сертификат плохой! Но я проверяю дважды, чтобы убедиться, что встроенный в openssl s_client выглядит так:

openssl s_client -connect sub.example.com:443

и что я получу:

CONNECTED(00000003)
---
Certificate chain
<snip>
Verify return code: 0 (ok)

Итак, теперь мы переходим к моему вопросу. OpenSSL (версия командной строки) говорит, что сертификат хорош. OpenSSL (библиотека Ruby) не согласен. Все мои браузеры говорят, что сертификат хорош.

Несколько дополнительных деталей, которые могут быть полезны. Сертификат является подстановочным знаком, но действителен для домена. Openssl s_client был запущен на той же машине за секунды, кроме кода Ruby. Это Ruby 1.8.7 p357, который устанавливается вместе с RVM.

Использует ли Ruby что-то иное, чем пакет CA, предоставляемый ОС хоста? Есть ли способ указать Ruby использовать определенный CA-пакет или системный?

53
Sam Stelfox

Если вас интересует только то, как заставить Ruby вести себя так же, как OpenSSL s_client или ваш браузер, вы можете перейти к самому последнему разделу, я опишу мелкий шрифт в следующем.

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

  • Store # add_file принимает путь к сертификату в кодировке PEM/DER
  • Store # add_cert принимает экземпляр X509 :: Certificate
  • Store # add_path принимает путь к каталогу, где можно найти доверенные сертификаты 

«Браузерный» подход

Это в отличие от подхода, который используют браузеры, Java (cacerts) или Windows с собственным внутренним хранилищем доверенных сертификатов. Там программное обеспечение предварительно оснащено набором доверенных сертификатов, которые, по мнению поставщика программного обеспечения, считаются «хорошими». Обычно это неплохая идея, но если вы действительно посмотрите на эти наборы, то вскоре заметите, что сертификатов слишком много. Человек не может действительно сказать, следует ли доверять всем этим сертификатам вслепую или нет. 

Рубиновый подход

С другой стороны, требования вашего типичного приложения на Ruby сильно отличаются от требований браузера. Браузер должен быть в состоянии позволить вам перейти на любой «законный» веб-сайт, который поставляется с сертификатом TLS и обслуживается по протоколу https. Но в типичном Ruby-приложении вам придется иметь дело только с несколькими службами, которые используют TLS или иным образом требуют проверки сертификата. 

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

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

Это неудобство привело к множеству сомнительных мер, чтобы обойти его, худшее из всего, что глобально установить OpenSSL::SSL::VERIFY_PEER = OpenSSL::SSL::VERIFY_NONE. Пожалуйста, не делай этого. Мы даже пошутили о добавлении кода, который позволяет случайному сбою вашего приложения, если мы столкнемся с этим хаком :)

Если ручная настройка доверия кажется слишком сложной, сейчас я предложу простую альтернативу, которая заставляет расширение OpenSSL вести себя точно так же, как команды CLI OpenSSL, такие как s_client.

Почему s_client может проверить сертификат

OpenSSL использует аналогичный подход к браузерам и Windows. Типичная установка помещает пакет доверенных сертификатов где-то на вашем жестком диске (что-то вроде /etc/ssl/certs/ca-bundle.crt), и это будет служить набором доверенных сертификатов по умолчанию. Вот где s_client смотрит, когда ему нужно проверить сертификаты пиров, и поэтому ваш эксперимент был успешным.

Заставить Ruby вести себя как s_client

Если вы все еще хотите иметь такой же комфорт при проверке сертификатов с помощью Ruby, вы можете указать ему использовать пакет доверенных сертификатов OpenSSL, если он доступен в вашей системе, вызвав OpenSSL::X509::Store#set_default_paths. Дополнительную информацию можно найти здесь . Чтобы использовать это с XMLRPC::Client, просто убедитесь, что set_default_paths вызывается для используемого X509::Store.

110
emboss

Если у вас есть файл CA-Certificates, просто сделайте это:

http.ca_file = <YOUR CA-CERT FILE PATH>
http.verify_mode = OpenSSL::SSL::VERIFY_PEER
http.verify_depth = 5
0
Guihen