Python Django 错误:
<urlopen error [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed:
unable to get local issuer certificate (_ssl.c:1131)>
异常位置:/usr/lib/python3.8/urllib/request.py,第 1357 行,在 do_open
运行:Python 3.8.10、Django 4.0.3、Ubuntu 20.04、Apache 2
我正在将 Django 用于一个简单的联系表单应用程序,该应用程序目前可以正常工作并且不会引发错误。当我使用此 Django 库https://github.com/tiesjan/django-hcaptcha-field将 hCaptcha 添加到表单时,会发生 CERTIFICATE_VERIFY_FAILED 错误。
问题似乎与 django-hcaptcha-field 无关;访问 hCaptcha API 时,它似乎是 Ubuntu 和 SSL 证书。
我已经查看了关于 SO 的多个问题(尤其是https://stackoverflow.com/questions/27835619/urllib-and-ssl-certificate-verify-failed-error,即使它是针对 OS X 的)并询问 Ubuntu证书问题.
我已经尝试了所有这些“修复”:
pip install pyOpenSSL --upgrade
apt-get install --reinstall python3-certifi
pip install --upgrade certifi --force
apt install --reinstall openssl
apt install ca-certificates
update-ca-certificates --fresh
export SSL_CERT_DIR=/etc/ssl/certs
我“强制”更新了我的 Let's Encrypt SSL。
我尝试了更新的证书:
wget --quiet https://curl.haxx.se/ca/cacert.pem
export SSL_CERT_FILE=$HOME/cacert.pem
我的 Python 代码中没有任何内容需要import ssl
我还能尝试什么?
诊断输出:
dpkg -l | grep cert
返回
ica-certificates 20210119~20.04.2 all Common CA certificates
certbot 0.40.0-1ubuntu0.1 all automatically configure HTTPS using Let's Encrypt
dirmngr 2.2.19-3ubuntu2.1 amd64 GNU privacy guard - network certificate management service
python-certbot-apache 0.36.0-1 all transitional dummy package
python3-certbot 0.40.0-1ubuntu0.1 all main library for certbot
python3-certbot-apache 0.39.0-1 all Apache plugin for Certbot
ipython3-certifi 2019.11.28-1 all root certificates for validating SSL certs and verifying TLS hosts (python3)
ssl-cert 1.0.39 all simple debconf wrapper for OpenSSL
dpkg -l | grep openssl
返回
libxmlsec1-openssl:amd64 1.2.28-2 amd64 Openssl engine for the XML security library
openssl 1.1.1f-1ubuntu2.12 amd64 Secure Sockets Layer toolkit - cryptographic utility
perl-openssl-defaults:amd64 4 amd64 version compatibility baseline for Perl OpenSSL packages
python3-openssl 19.0.0-1build1 all Python 3 wrapper around the OpenSSL library
whereis openssl
返回
openssl: /usr/bin/openssl /usr/local/bin/openssl /usr/include/openssl /usr/share/man/man1/openssl.1ssl.gz
which openssl /usr/bin/openssl
返回
/usr/local/bin/openssl
/usr/bin/openssl
ldd $(which wget)
返回
linux-vdso.so.1 (0x00007ffd9f10f000)
libpcre2-8.so.0 => /lib/x86_64-linux-gnu/libpcre2-8.so.0 (0x00007efdb3e3d000)
libuuid.so.1 => /lib/x86_64-linux-gnu/libuuid.so.1 (0x00007efdb3e34000)
libidn2.so.0 => /lib/x86_64-linux-gnu/libidn2.so.0 (0x00007efdb3e12000)
libssl.so.1.1 => /usr/local/lib/libssl.so.1.1 (0x00007efdb3d7a000)
libcrypto.so.1.1 => /usr/local/lib/libcrypto.so.1.1 (0x00007efdb3a8e000)
libz.so.1 => /lib/x86_64-linux-gnu/libz.so.1 (0x00007efdb3a72000)
libpsl.so.5 => /lib/x86_64-linux-gnu/libpsl.so.5 (0x00007efdb3a5d000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007efdb386b000)
libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007efdb3848000)
/lib64/ld-linux-x86-64.so.2 (0x00007efdb3f6b000)
libunistring.so.2 => /lib/x86_64-linux-gnu/libunistring.so.2 (0x00007efdb36c6000)
libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007efdb36c0000)
dpkg -l | grep python3-certifi
返回
python3-certifi 2019.11.28-1 all root certificates for validating SSL certs and verifying TLS hosts (python3)
追溯:
Request Method: POST
Request URL: https://example.com/contact/contact/contact/
Django Version: 4.0.3
Python Version: 3.8.10
Installed Applications:
['django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'contactform.apps.ContactformConfig',
'encrypted_files',
'hcaptcha_field']
Installed Middleware:
['django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware']
Traceback (most recent call last):
File "/usr/lib/python3.8/urllib/request.py", line 1354, in do_open
h.request(req.get_method(), req.selector, req.data, headers,
File "/usr/lib/python3.8/http/client.py", line 1256, in request
self._send_request(method, url, body, headers, encode_chunked)
File "/usr/lib/python3.8/http/client.py", line 1302, in _send_request
self.endheaders(body, encode_chunked=encode_chunked)
File "/usr/lib/python3.8/http/client.py", line 1251, in endheaders
self._send_output(message_body, encode_chunked=encode_chunked)
File "/usr/lib/python3.8/http/client.py", line 1011, in _send_output
self.send(msg)
File "/usr/lib/python3.8/http/client.py", line 951, in send
self.connect()
File "/usr/lib/python3.8/http/client.py", line 1425, in connect
self.sock = self._context.wrap_socket(self.sock,
File "/usr/lib/python3.8/ssl.py", line 500, in wrap_socket
return self.sslsocket_class._create(
File "/usr/lib/python3.8/ssl.py", line 1040, in _create
self.do_handshake()
File "/usr/lib/python3.8/ssl.py", line 1309, in do_handshake
self._sslobj.do_handshake()
During handling of the above exception ([SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate (_ssl.c:1131)), another exception occurred:
File "/usr/local/lib/python3.8/dist-packages/django/core/handlers/exception.py", line 55, in inner
response = get_response(request)
File "/usr/local/lib/python3.8/dist-packages/django/core/handlers/base.py", line 197, in _get_response
response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "/var/www/html/example.com/public_html/contact/contactform/views.py", line 26, in contact
if form.is_valid():
File "/usr/local/lib/python3.8/dist-packages/django/forms/forms.py", line 205, in is_valid
return self.is_bound and not self.errors
File "/usr/local/lib/python3.8/dist-packages/django/forms/forms.py", line 200, in errors
self.full_clean()
File "/usr/local/lib/python3.8/dist-packages/django/forms/forms.py", line 433, in full_clean
self._clean_fields()
File "/usr/local/lib/python3.8/dist-packages/django/forms/forms.py", line 445, in _clean_fields
value = field.clean(value)
File "/usr/local/lib/python3.8/dist-packages/django/forms/fields.py", line 199, in clean
self.validate(value)
File "/usr/local/lib/python3.8/dist-packages/hcaptcha_field/fields.py", line 129, in validate
response = opener.open(request, timeout=hcaptcha_settings.TIMEOUT)
File "/usr/lib/python3.8/urllib/request.py", line 525, in open
response = self._open(req, data)
File "/usr/lib/python3.8/urllib/request.py", line 542, in _open
result = self._call_chain(self.handle_open, protocol, protocol +
File "/usr/lib/python3.8/urllib/request.py", line 502, in _call_chain
result = func(*args)
File "/usr/lib/python3.8/urllib/request.py", line 1397, in https_open
return self.do_open(http.client.HTTPSConnection, req,
File "/usr/lib/python3.8/urllib/request.py", line 1357, in do_open
raise URLError(err)
Exception Type: URLError at /contact/contact/
Exception Value: <urlopen error [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate (_ssl.c:1131)>
https://github.com/tiesjan/django-hcaptcha-field的 hcaptcha_field 的 fields.py
import json
import logging
import ssl # added #############
import certifi # added #############
from urllib.error import HTTPError
from urllib.parse import urlencode
from urllib.request import build_opener, Request, ProxyHandler
from django import forms
from django.core.exceptions import ValidationError
from django.utils.translation import gettext_lazy as _
from hcaptcha_field.settings import hcaptcha_settings
from hcaptcha_field.widgets import hCaptchaWidget
LOGGER = logging.getLogger('hcaptcha_field')
DATA_ATTRIBUTE_CONFIG = frozenset([
'theme',
'size',
'tabindex',
'callback',
'expired-callback',
'chalexpired-callback',
'open-callback',
'close-callback',
'error-callback',
])
QUERY_PARAMETER_CONFIG = frozenset([
'onload',
'render',
'hl',
'recaptchacompat'
])
class hCaptchaField(forms.Field):
widget = hCaptchaWidget
default_error_messages = {
'error_hcaptcha': _(
# Translators: Error shown when an internal server error occurred.
'Something went wrong while verifying the hCaptcha. '
'Please try again.'
),
'invalid_hcaptcha': _(
# Translators: Error shown when visitor did not pass the hCaptcha check.
'hCaptcha could not be verified.'
),
'required': _(
# Translators: Error shown when visitor forgot to fill in the hCaptcha.
'Please prove you are human.'
),
}
def __init__(self, sitekey=None, **kwargs):
"""
Initializer for `hCaptchaField` class. It determines data attributes
for the widget class and constructs a widget if none is given. This
constructed widget receives the URL of the JavaScript resource for the
hCaptcha integration and the `sitekey` of the site to protect.
"""
# Retrieve settings
DEFAULT_CONFIG = hcaptcha_settings.DEFAULT_CONFIG
JS_API_URL = hcaptcha_settings.JS_API_URL
SITEKEY = hcaptcha_settings.SITEKEY
# Determine widget data attributes
self.widget_data_attrs = {}
for setting in DATA_ATTRIBUTE_CONFIG:
if setting in kwargs:
self.widget_data_attrs[setting] = kwargs.pop(setting)
elif setting in DEFAULT_CONFIG:
self.widget_data_attrs[setting] = DEFAULT_CONFIG[setting]
# If the `widget` argument is not given, instantiate `self.widget` with
# the hCaptcha API url and the sitekey
if 'widget' not in kwargs:
# Determine hCaptcha API url
query_params = {}
for setting in QUERY_PARAMETER_CONFIG:
if setting in kwargs:
query_params[setting] = kwargs.pop(setting)
elif setting in DEFAULT_CONFIG:
query_params[setting] = DEFAULT_CONFIG[setting]
if query_params:
js_api_url = '%s?%s' % (JS_API_URL, urlencode(query_params))
else:
js_api_url = JS_API_URL
# Determine hCaptcha sitekey
self.sitekey = sitekey or SITEKEY
# Instantiate widget
kwargs['widget'] = self.widget(
js_api_url=js_api_url, sitekey=self.sitekey)
super().__init__(**kwargs)
def widget_attrs(self, widget):
"""
Returns the widget attributes, including all the data attributes
determined in the initializer.
"""
attrs = super().widget_attrs(widget)
for key, value in self.widget_data_attrs.items():
attrs['data-%s' % key] = value
return attrs
def validate(self, value):
"""
Validates the field by verifying the value of the hidden field
`h-captcha-response` with their API endpoint.
"""
super().validate(value)
# Build request
opener = build_opener(ProxyHandler(hcaptcha_settings.PROXIES))
post_data = urlencode({
'secret': hcaptcha_settings.SECRET,
'response': value,
'sitekey': self.sitekey,
}).encode('utf-8')
request = Request(hcaptcha_settings.VERIFY_URL, post_data)
# Perform request
try:
context=ssl.create_default_context(cafile=certifi.where()) # added ############
response = opener.open(request, timeout=hcaptcha_settings.TIMEOUT)
except HTTPError:
LOGGER.exception("Failed to verify response with hCaptcha API.")
raise ValidationError(
self.error_messages['error_hcaptcha'],
code='error_hcaptcha'
)
# Check response
response_data = json.loads(response.read().decode('utf-8'))
if not response_data.get('success'):
LOGGER.error("Failed to pass hCaptcha check: %s", response_data)
raise ValidationError(
self.error_messages['invalid_hcaptcha'],
code='invalid_hcaptcha'
)