Python SSL证书验证错误

70

我正在使用 requests 访问一个 RESTful API。一切似乎都正常。我可以进行身份验证,获取会话令牌,甚至对编写的用于该API的类中的方法进行单元测试。然后我尝试运行我的代码。

首先,这是我所做的调用。头部是静态的与会话相关的项目,在 init() 中设置。请求体根据文件中的数据动态构建,并传递到此函数中。所有的数据都是有效的。

response = requests.post(url, headers=(Requestheader), data=json.dumps((Requestbody)))
当我运行代码时,它会使用我提供的元数据更新100多条记录。大约在第150个条目附近,我会收到以下错误信息:
ssl.SSLCertVerificationError: [SSL:CERTIFICATE_VERIFY_FAILED]证书验证失败:证书链中存在自签名证书(_ssl.c:1045)
我的第一步是致电供应商,了解他们所有的Web服务器是否都有正确签名的证书,因为我认为他们正在对我进行负载均衡并找到一个配置错误的服务器。他们告诉我不是这种情况。
然后我在谷歌上搜索了这个消息并发现有一个verify kwarg选项,所以我尝试了:
response = requests.post(url, headers=Requestheader, data=json.dumps(Requestbody), verify=False)

我知道这不是长期理想的解决方案,但我想测试一下行为是否相同。结果是一样的。它运行了一段时间,然后抛出ssl错误。我以为verify=False的想法是它不会检查。

供应商建议我检查我正在使用的url,但它很好。如果有代理服务器或真正的中间人攻击引起问题,我认为我在失败之前不会看到这么多成功。我以为可能是会话超时,但那应该抛出401状态,而我的活动水平太高了,不会发生不活动超时。

我是python新手,也不是安全专家。欢迎提供建议。


哪个版本的 requests?你应该尝试添加更多的调试或捕获流量,以查看您获取的每个证书,并查看发生了什么变化。在所有情况下,verify=False 都不是应该保留的东西。 - Patrick Mevzek
7个回答

54

更新

正如评论中指出的那样,python-certifi-win32不再维护。

请改用可替代的库pip-system-certs

pip install pip-system-certs

已过时

如果您正在使用Windows,并且已经将CA导入到受信任的DB存储中,您可以安装python-certifi-win32软件包,它会自动使用来自受信任的DB存储的相同证书。

pip install python-certifi-win32

然后你的代码应该开始工作了
参见: Python Requests with wincertstore

https://gitlab.com/alelec/python-certifi-win32


3
不幸的是, python-certifi-win32 似乎已经停止维护。原作者安德鲁·利奇(Andrew Leech)建议转向他的另一个项目 pip-system-certs。使用 pip-system-certs (PyPI链接) 进行库的替换似乎可以无缝工作。 - Briareos386
1
pip install pip-system-certs 对我来说起作用了 => 我建议更新帖子,使其更清晰明了 - undefined

35
我在这里找到了解决方案:这里
将以下代码插入源文件开头即可:
import ssl

try:
    _create_unverified_https_context = ssl._create_unverified_context
except AttributeError:
    # Legacy Python that doesn't verify HTTPS certificates by default
    pass
else:
    # Handle target environment that doesn't support HTTPS verification
    ssl._create_default_https_context = _create_unverified_https_context

26
这不是一个明智的做法。requests库中有一个verify=False选项,可以关闭SSL验证。但这意味着你不再检查你正在通信的机器是否是你想要通信的机器。这将让你容易受到中间人攻击。我的最终解决方法是对调用供应商API的所有函数添加重试装饰器。 - Tim B
1
由于我正在 Docker 容器内部进行交流,而且不想自己添加一个在两个容器上都有效的 CA,因此这正是解决方案。 - Milad.Nozari

18

据我看,这个问题可能有三种解决方法:

  1. 证书没问题,代码有误。例如,在使用此解决方案中描述的预备请求时,可能会出现此问题。

    但是,根据您提供的代码片段,我并不认为是这种情况。接下来两种解决方法,您需要获取导致错误的URL,并探索它的证书(可以通过浏览器完成)。

  2. 证书无误,但签署证书的证书颁发机构未包含在requests库使用的CA列表中。在打开有问题的URL后,请检查其中的CA是否有效并且已包含在列表中。如果没有,请按照StackOverflow问题的回答将CA添加到requests库的受信任列表中。

  3. 证书无效或自签名。与第二种解决方法相同。

一般解决方案是将你的脚本放在 try except 语句中,并打印所有会导致错误的URL。然后,通过requests库逐个尝试请求它们,看是否出现问题。如果出现问题,则为第二种或第三种情况。如果没有问题,则尝试在已安装新的Python和requests的另一台机器上运行脚本。如果运行成功,则说明你的配置存在问题。


1
谢谢您的全面回复。我学到了一些东西。1)由于我在网站上调用了许多API,所以应该使用会话。2)这让我再次查看了证书链,看起来有两条路径。在第二条路径上有一个自签名证书。https://www.ssllabs.com/ssltest/analyze.html?d=api.riscnetworks.com 我将再次查看您提供的第一个链接,了解如何添加信任。 - Tim B

10

对我来说,只需要在请求中添加verify=False就足够了:

response = requests.request("POST", url, headers=headers, data=payload, verify=False)

希望这个非常简单的答案能够帮助任何人。


谢谢兄弟。这也解决了我的问题。 - Anderson Oliveira
5
这意味着,访问的 HTTPS 服务器的 SSL 证书将不会被检查,任何 HTTPS 服务器(甚至是恶意的)都可以呈现自签名或无效的 SSL 证书,而客户端不会受到警告。 - Mohammad Torkashvand

9

Debian用户请按照以下步骤操作:

下载证书

openssl s_client -showcerts -connect SERVER:443 </dev/null 2>/dev/null | sed -n -e '/BEGIN\ CERTIFICATE/,/END
CERTIFICATE/ p' > git-mycompany-com.pem

安装

pip install certifi

执行Python

Python 3.7.3 (default, Jan 22 2021, 20:04:44) [GCC 8.3.0] on linux 输入 "help", "copyright", "credits" 或者 "license" 查看更多信息。

import certifi

certifi.where() '/home/user/detectaEnv/lib/python3.7/site-packages/certifi/cacert.pem'

exit()

执行此命令以将权限添加到Python

cat git-mycompany-com.pem | tee -a /home/user/detectaEnv/lib/python3.7/site-packages/certifi/cacert.pem

这对我完美地起作用了。


1
这是正确的方法。我已经使用mitmproxy和conda Python 3.10在Ubuntu 22.04上进行了验证。 - Jingguo Yao

4

您可以通过执行以下命令将资源URL设置为可信主机:

在此示例中,我已安装jupyterlab包。

pip install --trusted-host pypi.org --trusted-host files.pythonhosted.org jupyterlab

0

对于那些遇到问题并使用 Gmail 帐户下载某些文件或用于生成某些机密文件的人,请尝试使用 *@gmail.com 以外的另一个电子邮件帐户。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接