公司网络环境中使用Python requests 库SSL认证失败

逻辑北海 / 2024-09-04 / 原文

问题描述

使用python request库访问一个https url会报错,提示自签名错误。但是在浏览器中访问这个url就没问题,在私人电脑上也没问题。

Sample code:

import requests
import certifi

print(certifi.where())
response = requests.get('https://chroma-onnx-models.s3.amazonaws.com/all-MiniLM-L6-v2/onnx.tar.gz', verify=certifi.where())

报错如下:

......
requests.exceptions.SSLError: HTTPSConnectionPool(host='chroma-onnx-models.s3.amazonaws.com', port=443): Max retries exceeded with url: /all-MiniLM-L6-v2/onnx.tar.gz (Caused by SSLError(SSLCertVerificationError(1, '[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: self-signed certificate in certificate chain (_ssl.c:1006)')))

原因分析

Python requests 库没有使用系统自带的证书管理器。这意味着它建立的证书链可能跟同一台电脑上的浏览器不一样。
Copilot说,可能是因为公司的代理服务器检查了HTTPS流量,并且生成了一个自签名证书返回给 Python requests 客户端。因为公司的根证书不在 Python 的证书库中,所以代理服务器的证书不能被认证。
在私人电脑上没这个问题,应该是因为没有代理在中间生成一个自签名证书。
所以,解决方案是添加你公司的根证书到 Python 正在用的 PEM 文件,目的似乎是使代理服务器的证书可以被认证,不再是自签名。这个根证书可以在你的浏览器中查看。
可以尝试pip install pip_system_certs,或者手动将公司的ROOT CA添加到...\Lib\site-packages\certifi\cacert.pem

拓展阅读

公司的代理服务器如何影响HTTPS流量?
(From copilot)

在公司网络中,代理服务器可能会拦截和检查 HTTPS 流量,这种行为通常被称为“中间人攻击”(Man-in-the-Middle Attack, MITM),但在企业环境中,这种技术被合法地用于监控和保护网络安全。以下是详细解释:

  1. 代理服务器的工作原理
    代理服务器充当客户端(如你的计算机)和目标服务器(如你访问的网站)之间的中介。当你在公司网络中访问 HTTPS 网站时,请求首先发送到代理服务器,然后代理服务器再将请求转发给目标服务器。

  2. SSL/TLS 加密
    HTTPS 使用 SSL/TLS 协议来加密数据传输,确保数据在传输过程中不被窃取或篡改。正常情况下,客户端和目标服务器之间建立一个安全的加密通道。

  3. 代理服务器的拦截和检查
    为了检查 HTTPS 流量,代理服务器会在客户端和目标服务器之间插入自己。具体步骤如下:

代理服务器生成自签名证书:代理服务器会为目标网站生成一个自签名证书,并将其发送给客户端。
客户端信任代理服务器的证书:如果客户端信任代理服务器的证书(通常通过在客户端设备上预装代理服务器的根证书),客户端会接受这个自签名证书,并与代理服务器建立加密连接。
代理服务器与目标服务器建立连接:代理服务器再与目标服务器建立一个独立的加密连接。
4. 证书验证失败的原因
由于代理服务器使用的是自签名证书,而不是目标服务器的真实证书,客户端在验证证书时可能会失败,具体原因包括:

缺少信任:如果客户端没有预装代理服务器的根证书,客户端会认为代理服务器的证书不可信,从而导致 SSL 证书验证失败。
证书链不完整:代理服务器生成的自签名证书可能不包含完整的证书链,导致验证失败。
证书不匹配:客户端期望的证书与代理服务器提供的证书不匹配,导致验证失败。
解决方法
安装代理服务器的根证书:在客户端设备上安装并信任代理服务器的根证书。
配置代理设置:确保在使用 requests 库时正确配置代理设置,并指定正确的证书路径。