Игнорировать проверку сертификата с urllib3

Я использую urllib3 против частных служб с самоподписанными сертификатами. Есть ли способ заставить urllib3 игнорировать ошибки сертификата и все равно делать запрос?

import urllib3
c = urllib3.HTTPSConnectionPool('10.0.3.168', port=9001)
c.request('GET', '/')

При использовании:

import urllib3
c = urllib3.HTTPSConnectionPool('10.0.3.168', port=9001, cert_reqs='CERT_NONE')
c.request('GET', '/')

Возникает следующая ошибка:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib/python3/dist-packages/urllib3/request.py", line 67, in request
    **urlopen_kw)
  File "/usr/lib/python3/dist-packages/urllib3/request.py", line 80, in request_encode_url
    return self.urlopen(method, url, **urlopen_kw)
  File "/usr/lib/python3/dist-packages/urllib3/connectionpool.py", line 415, in urlopen
    body=body, headers=headers)
  File "/usr/lib/python3/dist-packages/urllib3/connectionpool.py", line 267, in _make_request
    conn.request(method, url, **httplib_request_kw)
  File "/usr/lib/python3.3/http/client.py", line 1061, in request
    self._send_request(method, url, body, headers)
  File "/usr/lib/python3.3/http/client.py", line 1099, in _send_request
    self.endheaders(body)
  File "/usr/lib/python3.3/http/client.py", line 1057, in endheaders
    self._send_output(message_body)
  File "/usr/lib/python3.3/http/client.py", line 902, in _send_output
    self.send(msg)
  File "/usr/lib/python3.3/http/client.py", line 840, in send
    self.connect()
  File "/usr/lib/python3/dist-packages/urllib3/connectionpool.py", line 103, in connect
    match_hostname(self.sock.getpeercert(), self.host)
  File "/usr/lib/python3/dist-packages/urllib3/packages/ssl_match_hostname/__init__.py", line 32, in match_hostname
    raise ValueError("empty or no certificate")
ValueError: empty or no certificate

Используя cURL, я могу получить ожидаемый ответ от службы

$ curl -k https://10.0.3.168:9001/
Please read the documentation for API endpoints

person Marco Ceppi    schedule 05.08.2013    source источник


Ответы (4)


Попробуйте следующий код:

import urllib3
c = urllib3.HTTPSConnectionPool('10.0.3.168', port=9001, cert_reqs='CERT_NONE',
                                assert_hostname=False)
c.request('GET', '/')

См. Установка assert_hostname значения False отключит проверку имени хоста SSL

person falsetru    schedule 05.08.2013
comment
Та же ошибка, что и выше. Скорее, похоже, что я использую более старую версию urllib3, 1.5, как это предусмотрено в Ubuntu. Даже если этот коммит еще не вошел в выпуск (последний выпуск был 1.6, который был 3 месяца назад, это слияние было 2 месяца назад) - person Marco Ceppi; 05.08.2013
comment
@MarcoCeppi Я выложу новый релиз на следующей неделе. У нас только что была решена пара крупных нерешенных проблем, которые мне нужно исправить перед публикацией. Тем не менее, пока вы можете использовать основную ветку, она должна быть стабильной. - person shazow; 05.08.2013
comment
@shazow Спасибо за это, к сожалению, я связан тем, что есть в архивах Ubuntu, так что это не повлияет на меня в течение некоторого времени. Я принял этот ответ как решение, но в конце концов мне пришлось переработать нашу инфраструктуру, чтобы обойти эту проблему. - person Marco Ceppi; 05.08.2013
comment
@MarcoCeppi Извините за проблемы. :) Постараюсь за вас ускорить. Также после выхода новой версии assert_hostname также является параметром конструктора, iirc. Таким образом, вам не понадобится дополнительная строка. - person shazow; 06.08.2013
comment
Если кто-то придет: это тоже работает как urllib3.PoolManager(cert_reqs='CERT_NONE') - person gies0r; 18.09.2019

Я нашел ответ на свою проблему. Документация urllib3 фактически не объясняет полностью, как подавить проверку сертификата SSL. Отсутствует ссылка на ssl.CERT_NONE.

В моем коде есть логическое значение ssl_verify, указывающее, нужна ли мне проверка SSL. Теперь код выглядит так:

import ssl
import urllib3

#
#
#
    if (ssl_verify):
        cert_reqs = ssl.CERT_REQUIRED
    else:
        cert_reqs = ssl.CERT_NONE
        urllib3.disable_warnings()

    http = urllib3.PoolManager(cert_reqs = cert_reqs)

    auth_url = f'https://{fmc_ip}/api/fmc_platform/v1/auth/generatetoken'
    type = {'Content-Type': 'application/json'}

    auth = urllib3.make_headers(basic_auth=f'{username}:{password}')
    headers = { **type, **auth }

    resp = http.request('POST',
                    auth_url,
                    headers=headers,
                    timeout=10.0)
person Pat    schedule 30.06.2020

Попробуйте создать пул соединений следующим образом:

HTTPSConnectionPool(self.host, self.port, cert_reqs=ssl.CERT_NONE)

или так:

HTTPSConnectionPool(self.host, self.port, cert_reqs='CERT_NONE')

Источник: https://github.com/shazow/urllib3/blob/master/test/with_dummyserver/test_https.py


ИЗМЕНИТЬ (после просмотра вашего редактирования):

Похоже, удаленный хост не отправил сертификат (возможно ли это?). Это код (из urllib3), который вызвал исключение:

def match_hostname(cert, hostname):
    """Verify that *cert* (in decoded format as returned by
SSLSocket.getpeercert()) matches the *hostname*. RFC 2818 rules
are mostly followed, but IP addresses are not accepted for *hostname*.

CertificateError is raised on failure. On success, the function
returns nothing.
"""
    if not cert:
        raise ValueError("empty or no certificate")

Таким образом, cert выглядит пустым, что означает, что self.sock.getpeercert() вернул пустую строку.

person Xaqq    schedule 05.08.2013
comment
Это действительно странно. Он отправляет или должен отправить сертификат обратно. Сервисы работают через браузеры. Я имею в виду, что это самозаверяющий сертификат, созданный пакетом Debian ssl-certs. - person Marco Ceppi; 05.08.2013

На этот вопрос я вижу много ответов, но, ИМХО, слишком много ненужной информации, которая может привести к путанице.

Просто добавьте параметр cert_reqs='CERT_NONE'

import urllib3
http = urllib3.PoolManager(cert_reqs='CERT_NONE')
person freedev    schedule 16.02.2021