urllib и ошибка "SSL: CERTIFICATE_VERIFY_FAILED"

Я получаю следующую ошибку:

Exception in thread Thread-3:
Traceback (most recent call last):
File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/threading.py", line 810, in        __bootstrap_inner
self.run()
File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/threading.py", line 763, in  run
self.__target(*self.__args, **self.__kwargs)
File "/Users/Matthew/Desktop/Skypebot 2.0/bot.py", line 271, in process
info = urllib2.urlopen(req).read()
File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/urllib2.py", line 154, in urlopen
return opener.open(url, data, timeout)
File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/urllib2.py", line 431, in open
response = self._open(req, data)
File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/urllib2.py", line 449, in _open
'_open', req)
File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/urllib2.py", line 409, in _call_chain
result = func(*args)
File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/urllib2.py", line 1240, in https_open
context=self._context)
File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/urllib2.py", line 1197, in do_open
raise URLError(err)
URLError: <urlopen error [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed (_ssl.c:581)>

Это код, который вызывает эту ошибку:

if input.startswith("!web"):
    input = input.replace("!web ", "")      
    url = "https://domainsearch.p.mashape.com/index.php?name=" + input
    req = urllib2.Request(url, headers={ 'X-Mashape-Key': 'XXXXXXXXXXXXXXXXXXXX' })
    info = urllib2.urlopen(req).read()
    Message.Chat.SendMessage ("" + info)

API, который я использую, требует от меня использования HTTPS. Как я могу сделать это в обход проверки?

48 ответов

Решение

Если вы просто хотите обойти проверку, вы можете создать новый SSLContext. По умолчанию вновь созданные контексты используют CERT_NONE.

Будьте осторожны с этим, как указано в разделе 17.3.7.2.1

При непосредственном вызове конструктора SSLContext по умолчанию используется CERT_NONE. Поскольку он не аутентифицирует другого партнера, он может быть небезопасным, особенно в режиме клиента, где большую часть времени вы хотели бы обеспечить аутентичность сервера, с которым вы разговариваете. Поэтому в режиме клиента настоятельно рекомендуется использовать CERT_REQUIRED.

Но если вы просто хотите, чтобы это работало сейчас, по какой-то другой причине, вы можете сделать следующее, вам придется import ssl также:

input = input.replace("!web ", "")      
url = "https://domainsearch.p.mashape.com/index.php?name=" + input
req = urllib2.Request(url, headers={ 'X-Mashape-Key': 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX' })
gcontext = ssl.SSLContext(ssl.PROTOCOL_TLSv1)  # Only for gangstars
info = urllib2.urlopen(req, context=gcontext).read()
Message.Chat.SendMessage ("" + info)

Это должно обойти вашу проблему, но вы не решаете ни одну из проблем, но вы не увидите [SSL: CERTIFICATE_VERIFY_FAILED] потому что вы сейчас не проверяете сертификат!

Чтобы добавить к вышесказанному, если вы хотите узнать больше о том, почему вы видите эти проблемы, вы должны взглянуть на PEP 476.

В этом PEP предлагается включить проверку подписей сертификатов X509, а также проверку имени хоста для клиентов Python HTTP по умолчанию при условии отказа для каждого вызова. Это изменение будет применено к Python 2.7, Python 3.4 и Python 3.5.

Есть рекомендованный отказ, который не отличается от моего совета выше:

import ssl

# This restores the same behavior as before.
context = ssl._create_unverified_context()
urllib.urlopen("https://no-valid-cert", context=context)

Он также имеет крайне обескураженную опцию с помощью monkeypatching, которую вы не часто видите в python:

import ssl

ssl._create_default_https_context = ssl._create_unverified_context

Который переопределяет функцию по умолчанию для создания контекста с помощью функции для создания непроверенного контекста.

Если вы хотите прочитать статью о том, почему не проверка сертификатов плоха в программном обеспечении, вы можете найти ее здесь!

Резюме

Запустите эту команду:

/Applications/Python\ 3.6/Install\ Certificates.command

контекст

Это не решение вашей конкретной проблемы, но я помещаю это здесь, потому что эта тема является лучшим результатом Google для "SSL: CERTIFICATE_VERIFY_FAILED", и это привело меня к безумной погоне за погоней.

Если вы установили Python 3.6 в OSX и получаете ошибку "SSL: CERTIFICATE_VERIFY_FAILED" при попытке подключиться к сайту https://, возможно, это связано с тем, что Python 3.6 в OSX вообще не имеет сертификатов и не может проверить любой SSL соединения. Это изменение для 3.6 в OSX, и оно требует шага после установки, который устанавливает certifi пакет сертификатов. Это задокументировано в ReadMe, который вы должны найти на /Applications/Python\ 3.6/ReadMe.rtf

ReadMe предложит вам запустить этот скрипт после установки, который просто устанавливает certifi: /Applications/Python\ 3.6/Install\ Certificates.command

Примечания к выпуску содержат дополнительную информацию: https://www.python.org/downloads/release/python-360/

Чтобы расширить ответ Крейга Гленни (извините, недостаточно репутации, чтобы комментировать):

в Python 3.6.1 на MacOs Sierra

Ввод этого в терминале bash решил проблему:

pip install certifi
/Applications/Python\ 3.6/Install\ Certificates.command

В Windows Python не смотрит на системный сертификат, он использует свой собственный, расположенный на ?\lib\site-packages\certifi\cacert.pem,

Решение вашей проблемы:

  1. скачать сертификат валидации домена в виде *.crt или *pem файла
  2. откройте файл в редакторе и скопируйте его содержимое в буфер обмена
  3. Найди свой cacert.pem место нахождения: from requests.utils import DEFAULT_CA_BUNDLE_PATH; print(DEFAULT_CA_BUNDLE_PATH)
  4. редактировать cacert.pem файл и вставьте свой сертификат проверки домена в конце файла.
  5. Сохраните файл и наслаждайтесь запросами!

У меня была похожая проблема, хотя я использовал urllib.request.urlopen в Python 3.4, 3.5 и 3.6. (Это часть эквивалента Python 3 urllib2 согласно примечанию во главе Python 2 urllib2 страница документации.)

Мое решение было pip install certifi установить certifi, у которого есть:

... тщательно отобранная коллекция корневых сертификатов для проверки надежности сертификатов SSL при проверке подлинности хостов TLS.

Затем в моем коде, где я ранее только что был:

import urllib.request as urlrq
resp = urlrq.urlopen('https://foo.com/bar/baz.html')

Я изменил это, чтобы:

import urllib.request as urlrq
import certifi
resp = urlrq.urlopen('https://foo.com/bar/baz.html', cafile=certifi.where())

Если я прочитал urllib2.urlopen документация правильно, она также имеет cafile аргумент. Так, urllib2.urlopen([...], certifi.where()) может работать и на Python 2.7.

Мое решение для Mac OS X:

1) Обновление до Python 3.6.5 с использованием собственного установщика Python, загруженного с официального веб-сайта языка Python https://www.python.org/downloads/

Я обнаружил, что этот установщик позаботится об обновлении ссылок и символических ссылок для нового Python намного лучше, чем homebrew.

2) Установите новый сертификат, используя "./Install Certificates.command", который находится в обновленном каталоге Python 3.6.

> cd "/Applications/Python 3.6/"
> sudo "./Install Certificates.command"

Вы можете попробовать добавить это к переменным вашей среды:

PYTHONHTTPSVERIFY=0 

Обратите внимание, что это отключит всю проверку HTTP, так что это немного похоже на кувалду, однако, если проверка не требуется, это может быть эффективным решением.

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

$ sudo update-ca-certificates --fresh
$ export SSL_CERT_DIR=/etc/ssl/certs

Я думаю, у вас есть несколько способов решить эту проблему. Я упомянул 5 способов ниже:

  1. Вы можете определить контекст для каждого запроса и передать контекст по каждому запросу для его использования, как показано ниже:
      import certifi
import ssl
import urllib
context = ssl.create_default_context(cafile=certifi.where())
result = urllib.request.urlopen('https://www.example.com', context=context)
  1. ИЛИ Установить файл сертификата вenvironment.
      import os
import certifi
import urllib
os.environ["REQUESTS_CA_BUNDLE"] = certifi.where()
os.environ["SSL_CERT_FILE"] = certifi.where()
result = urllib.request.urlopen('https://www.example.com')

  1. ИЛИ заменитьcreate default https contextметод:
      import certifi
import ssl
ssl._create_default_https_context = lambda: ssl.create_default_context(cafile=certifi.where())
result = urllib.request.urlopen('https://www.example.com')
  1. ИЛИ Если вы используете компьютер с Linux , создание новых сертификатов и экспорт переменной среды, указывающей на каталог сертификатов, исправили это.
      $ sudo update-ca-certificates --fresh
$ export SSL_CERT_DIR=/etc/ssl/certs
  1. ИЛИ Если вы используете компьютер Mac , создание новых сертификатов
      $ cd "/Applications/$(python3 --version | awk '{print $2}'| awk  -F. '{print "Python " $1"."$2}')"
$ sudo "./Install Certificates.command"
import requests
requests.packages.urllib3.disable_warnings()

import ssl

try:
    _create_unverified_https_context = ssl._create_unverified_context
except AttributeError:
    # Legacy Python that doesn't verify HTTPS certificates by default
    pass
else:
    # Handle target environment that doesn't support HTTPS verification
    ssl._create_default_https_context = _create_unverified_https_context

Взято отсюда https://gist.github.com/michaelrice/a6794a017e349fc65d01

Как я уже писал в комментарии, эта проблема, вероятно, связана с этим SO ответом.

Вкратце: существует несколько способов проверки сертификата. Проверка, используемая OpenSSL, несовместима с доверенными корневыми сертификатами, имеющимися в вашей системе. OpenSSL используется Python.

Вы можете попытаться получить недостающий сертификат для публичного первичного центра сертификации Verisign Class 3, а затем использовать cafile опция согласно документации Python:

urllib2.urlopen(req, cafile="verisign.pem")
$ cd $HOME
$ wget --quiet https://curl.haxx.se/ca/cacert.pem
$ export SSL_CERT_FILE=$HOME/cacert.pem

Источник: https://access.redhat.com/articles/2039753

Решение для Анаконды

Моя установка - Anaconda Python 3.7 на MacOS с прокси. Пути разные.

  • Вот как вы получаете правильный путь сертификатов:
import ssl
ssl.get_default_verify_paths()

который в моей системе производится

Out[35]: DefaultVerifyPaths(cafile='/miniconda3/ssl/cert.pem', capath=None,
 openssl_cafile_env='SSL_CERT_FILE', openssl_cafile='/miniconda3/ssl/cert.pem',
 openssl_capath_env='SSL_CERT_DIR', openssl_capath='/miniconda3/ssl/certs')

Как только вы узнаете, куда отправляется сертификат, вы соединяете сертификат, используемый прокси, в конец этого файла.

Я уже настроил conda для работы с моим прокси, запустив:

conda config --set ssl_verify <pathToYourFile>.crt

Если вы не помните, где находится ваш сертификат, вы можете найти его в ~/.condarc:

ssl_verify: <pathToYourFile>.crt

Теперь конкатенируйте этот файл до конца /miniconda3/ssl/cert.pemи запросы должны работать, и в частности sklearn.datasets и аналогичные инструменты должны работать.

Дальнейшие предостережения

Другие решения не работали, потому что установка Anaconda немного отличается:

  • Путь Applications/Python\ 3.X просто не существует

  • Путь, предоставленный командами ниже, является НЕПРАВИЛЬНЫМ путем

from requests.utils import DEFAULT_CA_BUNDLE_PATH
DEFAULT_CA_BUNDLE_PATH

Мне нужно добавить еще один ответ, потому что, как и Крейг Гленни, я продолжал погоню за гусиной кожей из-за множества постов, посвященных этой проблеме в Интернете.

Я использую MacPorts, и то, что я изначально думал, что это проблема Python, на самом деле было проблемой MacPorts: он не устанавливает корневой сертификат при установке openssl. Решение состоит в том, чтобы port install curl-ca-bundle, как уже упоминалось в этом сообщении в блоге.

В Python 2.7.12 (по умолчанию 29 июля 2016, 15:26:22) исправлена ​​упомянутая проблема. Эта информация может помочь кому-то еще.

Взгляни на

/ Приложения /Python 3.6/ Установить Certificates.command

Вы также можете перейти к разделу "Приложения" и нажать "Сертификаты"

Я удивлен, что все эти инструкции не решили мою проблему. Тем не менее, диагностика правильная (кстати, я использую Mac и Python3.6.1). Итак, подведем итог правильной части:

  • На Mac Apple выпускает OpenSSL
  • Python теперь использует собственный набор корневых сертификатов CA
  • Двоичная установка Python предоставила скрипт для установки корневого сертификата CA, необходимого Python ("/Applications/Python 3.6/Install Certificates.command")
  • Подробнее читайте в "/Applications/Python 3.6/ReadMe.rtf"

Для меня скрипт не работает, и все эти установки certifi и openssl тоже не удалось исправить. Может быть, потому что у меня есть несколько установок Python 2 и 3, а также много virtualenv. В конце мне нужно починить вручную.

pip install certifi   # for your virtualenv
mkdir -p /Library/Frameworks/Python.framework/Versions/3.6/etc/openssl
cp -a <your virtualenv>/site-package/certifi/cacert.pem \
  /Library/Frameworks/Python.framework/Versions/3.6/etc/openssl/cert.pem

Если это все еще не удается. Затем переустановите OpenSSL.

port install openssl

Я нашел это здесь

Я нашел это решение, вставьте этот код в начало вашего исходного файла:

import ssl

try:
   _create_unverified_https_context = ssl._create_unverified_context
except AttributeError:
    # Legacy Python that doesn't verify HTTPS certificates by default
    pass
else:
    # Handle target environment that doesn't support HTTPS verification
    ssl._create_default_https_context = _create_unverified_https_context

Этот код отменяет проверку, поэтому сертификация ssl не проверяется.

Ошибка SSL: CERTIFICATE_VERIFY_FAILED также может возникать из-за отсутствия промежуточного сертификата в ca-certificatesпакет в Linux. Например, в моем случае промежуточный сертификат " DigiCert SHA2 Secure Server CA " отсутствовал вca-certificatesпакет, даже если браузер Firefox включает его. Вы можете узнать, какой сертификат отсутствует, напрямую запустивwgetв URL-адресе, вызывающем эту ошибку. Затем вы можете найти соответствующую ссылку на файл CRT для этого сертификата на официальном сайте (например, https://www.digicert.com/digicert-root-certificates.htm в моем случае) центра сертификации. Теперь, чтобы включить сертификат, который отсутствует в вашем случае, вы можете запустить следующие команды, используя вместо этого ссылку для загрузки файла CRT:

wget https://cacerts.digicert.com/DigiCertSHA2SecureServerCA.crt

mv DigiCertSHA2SecureServerCA.crt DigiCertSHA2SecureServerCA.der

openssl x509 -inform DER -outform PEM -in DigiCertSHA2SecureServerCA.der -out DigicertSHA2SecureServerCA.pem.crt

sudo mkdir /usr/share/ca-certificates/extra

sudo cp DigicertSHA2SecureServerCA.pem.crt /usr/share/ca-certificates/extra/

sudo dpkg-reconfigure ca-certificates

После этого вы можете снова протестировать wget для вашего URL-адреса, а также с помощью python urllibпакет. Дополнительные сведения см. По адресу: https://bugs.launchpad.net/ubuntu/+source/ca-certificates/+bug/1795242.

Я опускаю голову в полус стыде, поскольку у меня была та же проблема, за исключением того, что в моем случае URL, который я нажимал, был действительным, сертификат был действительным. Недействительным было мое подключение к сети. Мне не удалось добавить данные прокси в браузер (в данном случае IE). Это остановило процесс проверки правильности.
Добавил в прокси подробности и мой питон был тогда очень доволен.

Для Python 3.4+ в Centos 6/7 , Fedora, просто установите доверенный CA следующим образом:

  1. Скопируйте CA.crt в /etc/pki/ca-trust/source/anchors/
  2. update-ca-trust force-enable
  3. update-ca-trust extract

Как и вы, я использую python 2.7 на моем старом iMac (OS X 10.6.8), я тоже столкнулся с проблемой, используя urllib2.urlopen:

urlopen error [SSL: CERTIFICATE_VERIFY_FAILED]

Мои программы работали нормально, без проблем с сертификатами SSL, и неожиданно (после загрузки программ) они вылетали с этой ошибкой SSL.

Проблема заключалась в используемой версии python:

  1. Нет проблем с https://www.python.org/downloads и python-2.7.9-macosx10.6.pkg

  2. проблема с той, которую установил инструмент Homebrew: "brew install python", версия находится в /usr/local/bin.

Глава под названием Certificate verification and OpenSSL [CHANGED for Python 2.7.9], в /Applications/Python 2.7/ReadMe.rtf объясняет проблему многими деталями.

Итак, проверьте, скачайте и вставьте в ваш PATH нужную версию python.

Решение для M1 MacOS

(Где Python был установлен с помощью доморощенного)

  1. Чтобы выяснить, какие пути используются для поиска сертификатов SSL. Запустите этот код Python:
      import ssl
ssl.get_default_verify_paths()

Для меня это вернулось:

DefaultVerifyPaths(cafile=None, capath=None,openssl_cafile_env='SSL_CERT_FILE',openssl_cafile='/opt/local/libexec/openssl3/etc/openssl/cert.pem',openssl_capath_env='SSL_CERT_DIR',openssl_capath='/opt/local/libexec/openssl3/etc/openssl/certs')

Это было неправильно.

  1. Переустановите OpenSSL
      brew uninstall --ignore-dependencies openssl && brew install openssl

Посмотрите на вывод приведенной выше команды. Здесь я увидел правильные пути для cert.pem и папки certs (использованной на шаге 3).

  1. Сообщите вашей системе о правильных путях. Установите следующие переменные среды:
      export SSL_CERT_FILE=/opt/homebrew/etc/openssl@3/cert.pem
export SSL_CERT_DIR=/opt/homebrew/etc/openssl@3/certs

(Добавьте эти строки в свой.profileфайл для постоянной установки переменных)

Python 2.7 на Amazon EC2 с CentOS 7

Я должен был установить переменную env SSL_CERT_DIR указать на мой ca-bundle который был расположен в /etc/ssl/certs/ca-bundle.crt

Установка PyOpenSSL с помощью pip работал для меня (без конвертации в PEM):

pip install PyOpenSSL

В моем случае я получал эту ошибку, потому что requests а также urllib3 версии были несовместимы, что приводило к следующей ошибке при установке:

ERROR: requests 2.21.0 has requirement urllib3<1.25,>=1.21.1, but you'll have urllib3 1.25 which is incompatible.
pip install 'urllib3<1.25' --force-reinstall

сделал свое дело.

Еще одно решение Анаконды. Я получал CERTIFICATE_VERIFY_FAILED в моей среде Python 2.7 на macOS. Оказывается, конда пути были плохими:

базовая (3.7) среда:

>>> import ssl
>>> ssl.get_default_verify_paths()
DefaultVerifyPaths(cafile='/usr/local/anaconda3/ssl/cert.pem', capath=None, openssl_cafile_env='SSL_CERT_FILE', openssl_cafile='/usr/local/anaconda3/ssl/cert.pem', openssl_capath_env='SSL_CERT_DIR', openssl_capath='/usr/local/anaconda3/ssl/certs')

2.7 окружение (пути не существовали!):

DefaultVerifyPaths(cafile='', capath=None, openssl_cafile_env='SSL_CERT_FILE', openssl_cafile='/usr/local/anaconda3/envs/py27/ssl/cert.pem', openssl_capath_env='SSL_CERT_DIR', openssl_capath='/usr/local/anaconda3/envs/py27/ssl/certs')

Исправление:

cd /usr/local/anaconda3/envs/py27/
mkdir ssl
cd ssl
ln -s ../../../ssl/cert.pem

Я решил эту проблему, закрыв проверку Fiddler (прокси-сервер отладки HTTP), если у вас включен прокси-сервер, и повторите попытку.

Пытаться

pip install --trusted-host pypi.python.org имя_пакета

Это сработало для меня.

Бывают случаи, когда нельзя использовать небезопасные соединения или передать контекст ssl в запрос urllib. Здесь мое решение на основе /questions/6105411/urllib-i-oshibka-ssl-certificateverifyfailed/6105462#6105462

В случае, если вы хотите использовать свой собственный файл сертификата

import ssl

def new_ssl_context_decorator(*args, **kwargs):
    kwargs['cafile'] = '/etc/ssl/certs/ca-certificates.crt'
    return ssl.create_default_context(*args, **kwargs)

ssl._create_default_https_context = ssl._create_unverified_context

или вы можете использовать общий файл из certifi

def new_ssl_context_decorator(*args, **kwargs):
    import certifi
    kwargs['cafile'] = certifi.where()
    return ssl.create_default_context(*args, **kwargs)
Другие вопросы по тегам