为什么python请求默认不使用系统ssl证书?
Why python requests not use the system ssl cert by default?
背景:
我正在 Ubuntu 18.04.1 LTS
,使用 next 安装自签名证书:
$ cp -rf my.crt /usr/local/share/ca-certificates/
$ update-ca-certificates
一切正常,因为现在我可以使用 next 成功访问我的网站了:
$ curl https://example.com
问题:
但是,当我使用python3 requests
访问时,报下一个错误:
>>> requests.get("https://example.com")
Traceback (most recent call last):
File "/usr/local/lib/python3.6/dist-packages/urllib3/connectionpool.py", line 706, in urlopen
chunked=chunked,
File "/usr/local/lib/python3.6/dist-packages/urllib3/connectionpool.py", line 382, in _make_request
self._validate_conn(conn)
File "/usr/local/lib/python3.6/dist-packages/urllib3/connectionpool.py", line 1010, in _validate_conn
conn.connect()
File "/usr/local/lib/python3.6/dist-packages/urllib3/connection.py", line 421, in connect
tls_in_tls=tls_in_tls,
File "/usr/local/lib/python3.6/dist-packages/urllib3/util/ssl_.py", line 429, in ssl_wrap_socket
sock, context, tls_in_tls, server_hostname=server_hostname
File "/usr/local/lib/python3.6/dist-packages/urllib3/util/ssl_.py", line 472, in _ssl_wrap_socket_impl
return ssl_context.wrap_socket(sock, server_hostname=server_hostname)
File "/usr/lib/python3.6/ssl.py", line 407, in wrap_socket
_context=self, _session=session)
File "/usr/lib/python3.6/ssl.py", line 817, in __init__
self.do_handshake()
File "/usr/lib/python3.6/ssl.py", line 1077, in do_handshake
self._sslobj.do_handshake()
File "/usr/lib/python3.6/ssl.py", line 689, in do_handshake
self._sslobj.do_handshake()
ssl.SSLError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed (_ssl.c:847)
补充:
但是,如果我明确指定 crt,它会成功运行:
>>> requests.get("https://example.com", verify='/etc/ssl/certs/ca-certificates.crt')
<Response [200]>
问题:
我正在使用第三方代码,这意味着我无法更改 python 代码。
所以,我的问题是:为什么 python 请求不默认在 /etc/ssl/certs/ca-certificates.crt
中查找 verify
?我认为这是 ubuntu 上的默认认证,因为 curl
默认情况下成功找到它...
而且,如果可能的话,我可以在 python 代码之外使用 export
设置任何变量,然后 python 请求将默认找到认证?
我找到了根本原因,requests
实际上会调用 where
使用 from certifi import where
在这台电脑上找到正确的 CA。
但是,在ubuntu中安装certifi
模块有两种方法:
选项 1:
apt-get install -y python3-certifi
选项 2:
pip3 install certifi
注意: 如果直接使用 pip3 install requests
,如果没有安装 [=22] 的 debian 软件包,它将使用 pip3
隐式安装 certifi
=].
然而,Looks Canonical(Ubuntu 的支持者)对 certifi
做了一些更改,所以如果使用 python3-certifi from apt
,def where
的代码是下一个或类似的不同版本不同:
root@4e1aab76e082:/# cat /usr/lib/python3/dist-packages/certifi/core.py
def where():
return "/etc/ssl/certs/ca-certificates.crt"
但如果使用pip3安装,则为:
root@4e1aab76e082:/# cat /usr/local/lib/python3.6/dist-packages/certifi/core.py
def where():
_CACERT_CTX = get_path("certifi", "cacert.pem")
这是此包中的 /usr/local/lib/python3.6/dist-packages/certifi/cacert.pem
。
所以ubuntu上的解决方案是:使用apt-get install python3-certifi
安装certifi
的ubuntu变体,卸载点一。然后,我们不能更改任何应用程序代码。
更新:
我找到了另一种与官方证书一起使用的方法,使用 next 变量,我还可以让 python requests module
从我指定的位置获取 CA,而无需更改应用程序代码:
export REQUESTS_CA_BUNDLE='/etc/ssl/certs/ca-certificates.crt'
背景:
我正在 Ubuntu 18.04.1 LTS
,使用 next 安装自签名证书:
$ cp -rf my.crt /usr/local/share/ca-certificates/
$ update-ca-certificates
一切正常,因为现在我可以使用 next 成功访问我的网站了:
$ curl https://example.com
问题:
但是,当我使用python3 requests
访问时,报下一个错误:
>>> requests.get("https://example.com")
Traceback (most recent call last):
File "/usr/local/lib/python3.6/dist-packages/urllib3/connectionpool.py", line 706, in urlopen
chunked=chunked,
File "/usr/local/lib/python3.6/dist-packages/urllib3/connectionpool.py", line 382, in _make_request
self._validate_conn(conn)
File "/usr/local/lib/python3.6/dist-packages/urllib3/connectionpool.py", line 1010, in _validate_conn
conn.connect()
File "/usr/local/lib/python3.6/dist-packages/urllib3/connection.py", line 421, in connect
tls_in_tls=tls_in_tls,
File "/usr/local/lib/python3.6/dist-packages/urllib3/util/ssl_.py", line 429, in ssl_wrap_socket
sock, context, tls_in_tls, server_hostname=server_hostname
File "/usr/local/lib/python3.6/dist-packages/urllib3/util/ssl_.py", line 472, in _ssl_wrap_socket_impl
return ssl_context.wrap_socket(sock, server_hostname=server_hostname)
File "/usr/lib/python3.6/ssl.py", line 407, in wrap_socket
_context=self, _session=session)
File "/usr/lib/python3.6/ssl.py", line 817, in __init__
self.do_handshake()
File "/usr/lib/python3.6/ssl.py", line 1077, in do_handshake
self._sslobj.do_handshake()
File "/usr/lib/python3.6/ssl.py", line 689, in do_handshake
self._sslobj.do_handshake()
ssl.SSLError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed (_ssl.c:847)
补充:
但是,如果我明确指定 crt,它会成功运行:
>>> requests.get("https://example.com", verify='/etc/ssl/certs/ca-certificates.crt')
<Response [200]>
问题:
我正在使用第三方代码,这意味着我无法更改 python 代码。
所以,我的问题是:为什么 python 请求不默认在 /etc/ssl/certs/ca-certificates.crt
中查找 verify
?我认为这是 ubuntu 上的默认认证,因为 curl
默认情况下成功找到它...
而且,如果可能的话,我可以在 python 代码之外使用 export
设置任何变量,然后 python 请求将默认找到认证?
我找到了根本原因,requests
实际上会调用 where
使用 from certifi import where
在这台电脑上找到正确的 CA。
但是,在ubuntu中安装certifi
模块有两种方法:
选项 1:
apt-get install -y python3-certifi
选项 2:
pip3 install certifi
注意: 如果直接使用 pip3 install requests
,如果没有安装 [=22] 的 debian 软件包,它将使用 pip3
隐式安装 certifi
=].
然而,Looks Canonical(Ubuntu 的支持者)对 certifi
做了一些更改,所以如果使用 python3-certifi from apt
,def where
的代码是下一个或类似的不同版本不同:
root@4e1aab76e082:/# cat /usr/lib/python3/dist-packages/certifi/core.py
def where():
return "/etc/ssl/certs/ca-certificates.crt"
但如果使用pip3安装,则为:
root@4e1aab76e082:/# cat /usr/local/lib/python3.6/dist-packages/certifi/core.py
def where():
_CACERT_CTX = get_path("certifi", "cacert.pem")
这是此包中的 /usr/local/lib/python3.6/dist-packages/certifi/cacert.pem
。
所以ubuntu上的解决方案是:使用apt-get install python3-certifi
安装certifi
的ubuntu变体,卸载点一。然后,我们不能更改任何应用程序代码。
更新:
我找到了另一种与官方证书一起使用的方法,使用 next 变量,我还可以让 python requests module
从我指定的位置获取 CA,而无需更改应用程序代码:
export REQUESTS_CA_BUNDLE='/etc/ssl/certs/ca-certificates.crt'