Когда я запрашиваю URL-адрес с просроченным сертификатом HTTPS, я не получаю значимой ошибки от запросов. Вместо этого он дает мне каскад «ssl.SSLError: A failure in the SSL library occurred».
См. этот пример с https://expired.badssl.com/ :
>python
Python 3.9.5 (tags/v3.9.5:0a7dcbd, May 3 2021, 17:27:52) [MSC v.1928 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> import requests
>>> requests.get("https://expired.badssl.com/")
Traceback (most recent call last):
File "C:\Users\me\apps\Python39\lib\site-packages\urllib3\connectionpool.py", line 670, in urlopen
httplib_response = self._make_request(
File "C:\Users\me\apps\Python39\lib\site-packages\urllib3\connectionpool.py", line 381, in _make_request
self._validate_conn(conn)
File "C:\Users\me\apps\Python39\lib\site-packages\urllib3\connectionpool.py", line 978, in _validate_conn
conn.connect()
File "C:\Users\me\apps\Python39\lib\site-packages\urllib3\connection.py", line 362, in connect
self.sock = ssl_wrap_socket(
File "C:\Users\me\apps\Python39\lib\site-packages\urllib3\util\ssl_.py", line 386, in ssl_wrap_socket
return context.wrap_socket(sock, server_hostname=server_hostname)
File "C:\Users\me\apps\Python39\lib\ssl.py", line 500, in wrap_socket
return self.sslsocket_class._create(
File "C:\Users\me\apps\Python39\lib\ssl.py", line 1040, in _create
self.do_handshake()
File "C:\Users\me\apps\Python39\lib\ssl.py", line 1309, in do_handshake
self._sslobj.do_handshake()
ssl.SSLError: A failure in the SSL library occurred (_ssl.c:1129)
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "C:\Users\me\apps\Python39\lib\site-packages\requests\adapters.py", line 439, in send
resp = conn.urlopen(
File "C:\Users\me\apps\Python39\lib\site-packages\urllib3\connectionpool.py", line 726, in urlopen
retries = retries.increment(
File "C:\Users\me\apps\Python39\lib\site-packages\urllib3\util\retry.py", line 446, in increment
raise MaxRetryError(_pool, url, error or ResponseError(cause))
urllib3.exceptions.MaxRetryError: HTTPSConnectionPool(host='expired.badssl.com', port=443): Max retries exceeded with url: / (Caused by SSLError(SSLError(1, 'A failure in the SSL library occurred (_ssl.c:1129)')))
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "C:\Users\me\apps\Python39\lib\site-packages\requests\api.py", line 76, in get
return request('get', url, params=params, **kwargs)
File "C:\Users\me\apps\Python39\lib\site-packages\requests\api.py", line 61, in request
return session.request(method=method, url=url, **kwargs)
File "C:\Users\me\apps\Python39\lib\site-packages\requests\sessions.py", line 530, in request
resp = self.send(prep, **send_kwargs)
File "C:\Users\me\apps\Python39\lib\site-packages\requests\sessions.py", line 643, in send
r = adapter.send(request, **kwargs)
File "C:\Users\me\apps\Python39\lib\site-packages\requests\adapters.py", line 514, in send
raise SSLError(e, request=request)
requests.exceptions.SSLError: HTTPSConnectionPool(host='expired.badssl.com', port=443): Max retries exceeded with url: / (Caused by SSLError(SSLError(1, 'A failure in the SSL library occurred (_ssl.c:1129)')))
>>>
Requests — это 2.24.0, а ssl.OPENSSL_VERSION написано «OpenSSL 1.1.1h 22 сентября 2020 года». Не могу обновить пакеты.
Как я могу получить осмысленную ошибку или сообщение об ошибке, в котором говорится, что срок действия сертификата истек?
Обычно вы справляетесь с такими вещами следующим образом:
import requests
try:
requests.get("https://expired.badssl.com/")
except requests.exceptions.SSLError as e:
print(f'oops! got the following SSL error: {e}')
И на моей тестовой машине вывод содержит [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: certificate has expired
Поскольку в вашем тексте исключения нет «сертификата, срок действия которого истек», я подозреваю, что у вас есть ошибка в вашей версии чего-то. requests 2.24.0 не последняя версия. Кроме того, запросы используют как urllib3, так и certifi. Возможно, вам стоит попробовать:
Pip install --upgrade urllib3 requests certifi
Требования:
Самым простым решением было бы обновить установленный OpenSSL, скажем, до текущей версии 1.1.1n, но даже 1.1.1i будет достаточно, чтобы получить сообщение, содержащее certificate has expired. Но обновление библиотеки OpenSSL невозможно, как вы упоминаете в своем посте.
Кроме того, вы можете перехватывать исключения во время запроса, а затем явно проверять, не истек ли срок действия сертификата сервера.
Для этого вы можете, например, взять функцию get_cert_for_hostname из моего предыдущего ответ на переполнение стека и соответственно сравнить поле not_valid_after с текущей датой.
Простой автономный пример может выглядеть примерно так:
import ssl
import requests
import platform
from datetime import datetime
from cryptography import x509
def get_cert_for_hostname(hostname, port):
conn = ssl.create_connection((hostname, port))
context = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
sock = context.wrap_socket(conn, server_hostname=hostname)
certDER = sock.getpeercert(True)
certPEM = ssl.DER_cert_to_PEM_cert(certDER)
conn.close()
return x509.load_pem_x509_certificate(certPEM.encode('ascii'))
def is_cert_expired(hostname, port):
cert = get_cert_for_hostname(hostname, port)
return datetime.now() > cert.not_valid_after
if __name__ == '__main__':
print(f"Python version: {platform.python_version()}")
print(f"OpenSSL version: {ssl.OPENSSL_VERSION}")
print(f"requests version: {requests.__version__}") #
hosts = ['software7.com', 'expired.badssl.com']
for host in hosts:
try:
requests.get(f"https://{host}")
print(f"request for host {host} was successful")
except BaseException as err:
if is_cert_expired(host, 443):
print(f"certificate for {host} expired")
else:
print(f"error {err} with {host}")
Затем в консоль отладки будет записано следующее:
Python version: 3.9.5
OpenSSL version: OpenSSL 1.1.1h 22 Sep 2020
requests version: 2.24.0
request for host software7.com was successful
certificate for expired.badssl.com expired