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
,
Решение вашей проблемы:
- скачать сертификат валидации домена в виде *.crt или *pem файла
- откройте файл в редакторе и скопируйте его содержимое в буфер обмена
- Найди свой
cacert.pem
место нахождения:from requests.utils import DEFAULT_CA_BUNDLE_PATH; print(DEFAULT_CA_BUNDLE_PATH)
- редактировать
cacert.pem
файл и вставьте свой сертификат проверки домена в конце файла. - Сохраните файл и наслаждайтесь запросами!
У меня была похожая проблема, хотя я использовал 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 способов ниже:
- Вы можете определить контекст для каждого запроса и передать контекст по каждому запросу для его использования, как показано ниже:
import certifi
import ssl
import urllib
context = ssl.create_default_context(cafile=certifi.where())
result = urllib.request.urlopen('https://www.example.com', context=context)
- ИЛИ Установить файл сертификата в
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')
- ИЛИ заменить
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')
- ИЛИ Если вы используете компьютер с Linux , создание новых сертификатов и экспорт переменной среды, указывающей на каталог сертификатов, исправили это.
$ sudo update-ca-certificates --fresh
$ export SSL_CERT_DIR=/etc/ssl/certs
- ИЛИ Если вы используете компьютер 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
Решение для Анаконды
Моя установка - 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 следующим образом:
- Скопируйте CA.crt в
/etc/pki/ca-trust/source/anchors/
update-ca-trust force-enable
update-ca-trust extract
Как и вы, я использую python 2.7 на моем старом iMac (OS X 10.6.8), я тоже столкнулся с проблемой, используя urllib2.urlopen:
urlopen error [SSL: CERTIFICATE_VERIFY_FAILED]
Мои программы работали нормально, без проблем с сертификатами SSL, и неожиданно (после загрузки программ) они вылетали с этой ошибкой SSL.
Проблема заключалась в используемой версии python:
Нет проблем с https://www.python.org/downloads и python-2.7.9-macosx10.6.pkg
проблема с той, которую установил инструмент 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 был установлен с помощью доморощенного)
- Чтобы выяснить, какие пути используются для поиска сертификатов 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')
Это было неправильно.
- Переустановите OpenSSL
brew uninstall --ignore-dependencies openssl && brew install openssl
Посмотрите на вывод приведенной выше команды. Здесь я увидел правильные пути для cert.pem и папки certs (использованной на шаге 3).
- Сообщите вашей системе о правильных путях. Установите следующие переменные среды:
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)