为什么python请求默认不使用系统ssl证书? [英] Why python requests not use the system ssl cert by default?

查看:45
本文介绍了为什么python请求默认不使用系统ssl证书?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

背景:

我正在使用 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")回溯(最近一次调用最后一次):文件/usr/local/lib/python3.6/dist-packages/urllib3/connectionpool.py",第 706 行,在 urlopen分块=分块,文件/usr/local/lib/python3.6/dist-packages/urllib3/connectionpool.py",第 382 行,在 _make_request 中self._validate_conn(conn)文件/usr/local/lib/python3.6/dist-packages/urllib3/connectionpool.py",第1010行,_validate_conn连接.connect()文件/usr/local/lib/python3.6/dist-packages/urllib3/connection.py",第421行,在connecttls_in_tls=tls_in_tls,文件/usr/local/lib/python3.6/dist-packages/urllib3/util/ssl_.py",第 429 行,在 ssl_wrap_socket袜子,上下文,tls_in_tls,server_hostname=server_hostname文件/usr/local/lib/python3.6/dist-packages/urllib3/util/ssl_.py",第472行,_ssl_wrap_socket_impl返回 ssl_context.wrap_socket(sock, server_hostname=server_hostname)文件/usr/lib/python3.6/ssl.py",第 407 行,在 wrap_socket_context=self,_session=session)文件/usr/lib/python3.6/ssl.py",第 817 行,在 __init__ 中self.do_handshake()文件/usr/lib/python3.6/ssl.py",第 1077 行,在 do_handshake 中self._sslobj.do_handshake()文件/usr/lib/python3.6/ssl.py",第 689 行,在 do_handshake 中self._sslobj.do_handshake()ssl.SSLError: [SSL: CERTIFICATE_VERIFY_FAILED] 证书验证失败 (_ssl.c:847)

附加:

但是,如果我明确指定 crt,它会成功运行:

<预><代码>>>>requests.get("https://example.com", verify='/etc/ssl/certs/ca-certificates.crt')<响应[200]>

问题:

我使用的是第三方代码,这意味着我无法更改 python 代码.

所以,我的问题是:为什么python请求不默认在/etc/ssl/certs/ca-certificates.crt中找到verify?我认为这是 ubuntu 上的默认认证,因为 curl 默认情况下成功找到了这个...

而且,如果可能的话,我可以使用 export 在 python 代码之外设置任何变量,那么 python 请求将默认查找认证?

解决方案

我找到了根本原因,requests 实际上会使用 from certifi 调用 where导入 where 在这台电脑上找到合适的 CA.

但是,在ubuntu中有两种安装certifi模块的方法:

选项 1:

apt-get install -y python3-certifi

选项 2:

pip3 安装证书

注意:如果直接使用pip3 install requests,如果没有debian,它将使用pip3隐式安装certifipython3-certifi 安装的包.

然而,看起来 Canonical(Ubuntu 的支持者)对 certifi 做了一些改变,所以如果使用 python3-certifi from aptdef 的代码在哪里 是 next 或相似的,因不同版本而异:

root@4e1aab76e082:/# cat/usr/lib/python3/dist-packages/certifi/core.py定义在哪里():返回/etc/ssl/certs/ca-certificates.crt";

但是如果使用pip3安装,就会:

root@4e1aab76e082:/# cat/usr/local/lib/python3.6/dist-packages/certifi/core.py定义在哪里():_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变种,卸载pip一.然后,我们无法更改任何应用代码.

更新:

我找到了另一种适用于官方证书的方法,使用 next 变量,我也可以让 python requests module 从我指定的位置获取 CA,而无需更改应用程序代码:

export REQUESTS_CA_BUNDLE='/etc/ssl/certs/ca-certificates.crt'

Background:

I'm working on Ubuntu 18.04.1 LTS, using next to install the self-signed certs:

$ cp -rf my.crt /usr/local/share/ca-certificates/
$ update-ca-certificates

Everything works fine, because now I could use next to visit my web successfully:

$ curl https://example.com

Problem:

However, when I use python3 requests to visit, it reports next error:

>>> 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)

Additional:

But, if I explicitly specify the crt, it works successfully:

>>> requests.get("https://example.com", verify='/etc/ssl/certs/ca-certificates.crt')
<Response [200]>

Question:

I'm using a third-party code, which means I can't change the python code.

So, my question is: why python requests not default to find verify in /etc/ssl/certs/ca-certificates.crt? It's the default certification on ubuntu I think, as curl successfully find this by default...

And, if possible I could set any variable with export which outside of python code, then python requests will default to find the certification?

解决方案

I find the root cause, the requests in fact will call where using from certifi import where to find the proper CA on this pc.

But, there are 2 kinds of ways to install certifi module in ubuntu:

Option 1:

apt-get install -y python3-certifi

Option 2:

pip3 install certifi

NOTE: if directly using pip3 install requests, it will implicitly install certifi using pip3 if no debian package installed for python3-certifi.

However, looks Canonical (the backers of Ubuntu) made some changes for certifi, so if using python3-certifi from apt, the code of def where is next or similar varies from different versions:

root@4e1aab76e082:/# cat /usr/lib/python3/dist-packages/certifi/core.py
def where():
    return "/etc/ssl/certs/ca-certificates.crt"

But if using pip3 to install, it will be:

root@4e1aab76e082:/# cat /usr/local/lib/python3.6/dist-packages/certifi/core.py
def where():
    _CACERT_CTX = get_path("certifi", "cacert.pem")

That's /usr/local/lib/python3.6/dist-packages/certifi/cacert.pem in this package.

So the solution on ubuntu is: use apt-get install python3-certifi to install the ubuntu variant of certifi, uninstall the pip one. Then, we can no change anything of app code.

UPDATE:

I find another way which works with official certifi, using next variable, I could also let python requests module go to fetch the CA from where I specified without change app code:

export REQUESTS_CA_BUNDLE='/etc/ssl/certs/ca-certificates.crt'

这篇关于为什么python请求默认不使用系统ssl证书?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆