python - 为什么python请求默认不使用系统ssl证书?

标签 python ubuntu ssl https python-requests

背景:
我正在处理 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 请求不默认查找 verify/etc/ssl/certs/ca-certificates.crt ?我认为这是 ubuntu 上的默认认证,如 curl默认成功找到这个...
而且,如果可能的话,我可以使用 export 设置任何变量在python代码之外,那么python请求会默认找到认证吗?

最佳答案

我找到了根本原因,requests实际上会调用where使用 from certifi import where在这台电脑上找到合适的 CA。
但是,有两种安装方式certifi ubuntu 中的模块:
选项 1:

apt-get install -y python3-certifi
选项 2:
pip3 install certifi
注意:如果直接使用 pip3 install requests ,它将隐式安装certifi使用 pip3如果没有为 python3-certifi 安装 debian 软件包.
然而,看起来 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 变体,卸载pip之一。然后,我们无法更改任何应用程序代码。
更新:
我找到了另一种与官方证书一起使用的方法,使用下一个变量,我也可以让 python requests module去从我指定的地方获取 CA 而不更改应用程序代码:
export REQUESTS_CA_BUNDLE='/etc/ssl/certs/ca-certificates.crt'

关于python - 为什么python请求默认不使用系统ssl证书?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/66848351/

相关文章:

python - 使用 joblib.load 从磁盘读取 xgboost 模型时出现类型错误

c++ - 未在此范围内声明 OpenCV 符号

linux - 我无法连接到正在运行的 ubuntu ec2 实例

python 2.7.5 请求和证书验证失败

python - Python 中的简单 Mandelbrot 集

python - 用于并行下载多个文件的库或工具

c - 如何使用 gedit/gcc 在 Ubuntu 中制作一个 C 项目文件或多个开源文件我是新手请指导我

ssl - FTP over HTTPS 远程服务器文件使用 WEBAPI 上传

Apache 2.2,(.htaccess 中的 MOD_SSL 和 Mod_rewrite

python dask DataFrame,支持(可简单并行化)行吗?