Python requests库 HTTPS 请求报SSL: CERTIFICATE_VERIFY_FAILED 完整解决方案

Python requests库 HTTPS 请求报SSL: CERTIFICATE_VERIFY_FAILED 完整解决方案
摘要
Python项目使用requests请求HTTPS接口,本地开发环境正常,部署到服务器环境直接抛出SSLError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed证书校验失败。网上大量教程直接关闭verify=False跳过校验,但是该做法会引入中间人攻击风险。本文区分场景,分析根因,给出生产环境安全可行的全套解决方案,覆盖开发调试、内网自签证书、服务器证书缺失、系统根证书异常等场景。
QQ20260812-153444.png
问题现象
同一套Python代码,在Windows开发电脑运行,请求HTTPS接口一切正常。部署到Linux服务器执行,调用requests.get直接抛出SSL证书验证失败。

报错完整堆栈:

requests.exceptions.SSLError: HTTPSConnectionPool(host='api.demo.com', port=443): Max retries exceeded with url: /api/data
Caused by SSLError(SSLCertVerificationError(1, '[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate (_ssl.c:997)'))

很多网上的临时解决方案,直接关闭证书校验:

 不推荐生产环境使用!存在中间人攻击风险
resp = requests.get("https://api.demo.com", verify=False)

加上verify=False之后请求可以成功,但是控制台大量告警,并且生产环境会有安全隐患。

复现步骤

  1. Linux服务器系统缺少根证书包,或者Python使用的证书链损坏。
  2. 目标服务使用内部自签发SSL证书,不是公开CA证书。
  3. Python虚拟环境,内置的certifi证书库损坏、版本老旧。
  4. 系统时间错误,时间偏移导致证书有效期校验失败。
  5. 内网环境存在代理,代理替换SSL证书。

    根因分析

  6. requests库默认开启SSL证书校验,需要本地存在可信根证书,用来验证远端服务器证书合法性。
  7. requests默认使用certifi包提供的根证书。如果certifi版本老旧、文件损坏,就无法识别新的CA证书。
  8. 部分Linux极简系统(docker镜像、最小化服务器)没有安装系统ca‑certificates系统证书包。
  9. 目标接口使用企业内部自签证书,公开根证书库里不存在对应的签发机构,校验直接失败。
  10. 服务器系统时间不正确,证书不在有效期,校验失败。
  11. 设置verify=False只是跳过校验,不会校验服务端证书,中间人可以劫持流量窃取请求数据,生产业务严禁直接使用该方案。

    分场景解决方案
    场景1:公网HTTPS域名,正常CA颁发证书,服务器报证书校验失败
    优先修复系统证书或者certifi库。

方案A:安装系统根证书包(Debian/Ubuntu)

apt update
apt install ca‑certificates
update‑ca‑certificates

CentOS/RHEL:

yum install ca‑certificates
update‑ca‑trust

方案B:升级certifi证书库

pip install --upgrade certifi

查看certifi证书路径,确认证书文件存在:

import certifi
print(certifi.where())

打印路径可以看到cacert.pem文件,确认文件没有丢失损坏。

requests优先读取certifi提供的cacert.pem,不是直接读取系统证书,虚拟环境下就算系统证书完整,虚拟环境certifi损坏依旧报错。

场景2:内网服务,使用自签证书(企业内部接口)
不能直接关闭verify,把自签证书pem文件传入verify参数。

  1. 将自签证书保存为myca.pem,放到项目目录。
  2. 请求的时候指定证书文件路径:
import requests

resp = requests.get("https://inner.api.demo.com", verify="./myca.pem")
print(resp.text)

这种方式会校验服务端证书,同时信任我们自己的CA,不会关闭SSL安全校验,适合生产内网环境。

场景3:全局指定证书,不用每次请求填写verify参数
可以设置环境变量REQUESTS_CA_BUNDLE,全局指定证书文件。

export REQUESTS_CA_BUNDLE=/opt/project/myca.pem

Python代码无需修改,所有requests请求自动加载该证书。

场景4:Docker最小镜像环境问题
很多docker python镜像裁剪掉系统证书,在Dockerfile增加安装证书包。

FROM python:3.11‑slim
RUN apt update && apt install -y ca‑certificates && rm -rf /var/lib/apt/lists/
COPY ./myca.pem /etc/ssl/certs/myca.pem
ENV REQUESTS_CA_BUNDLE=/etc/ssl/certs/myca.pem

场景5:系统时间错误导致证书校验失败
检查服务器系统时间

date

时间和现实时间差距过大,证书校验直接失败。同步系统时间:

timedatectl set‑ntp true

什么时候才可以使用verify=False?
仅仅仅限本地开发调试、临时测试,绝对禁止线上业务使用。
如果业务必须内网代理抓包调试,开发环境临时关闭校验,建议同时关闭告警。

import requests
import urllib3
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
resp = requests.get("https://test", verify=False)

上线前务必删除verify=False,不要把调试代码提交到生产。

排查调试手段

  1. 使用系统curl测试接口,确认是否服务器层面证书问题。
curl -v https://api.demo.com

curl同样报证书错误,说明系统层面证书、时间问题,不是Python代码问题。curl访问正常,说明Python虚拟环境certifi证书库异常。

  1. 打印certifi证书路径,检查文件是否存在。
  2. 确认Python是否使用虚拟环境,pip包是否和预期一致。

    踩坑总结

  3. 遇到SSL证书报错,不要第一时间复制网上verify=False的代码,这只是掩盖问题,留下安全漏洞。
  4. requests的证书来源优先certifi包,不是系统证书,虚拟环境中很容易出现证书缺失。
  5. 内网自签服务,把CA证书传入verify参数,保留SSL校验能力。
  6. docker精简镜像经常缺失ca‑certificates包,部署Python服务务必注意。
  7. 系统时间异常也会抛出证书校验失败,容易被忽略。

凡尘版权

友情链接:凡尘博客
凡尘博客文章|凡尘博客文摘
凡尘影院
凡尘乡音|凡尘街坊
凡尘博客|雨落凡尘博客|羽落凡尘博客
凡尘博客|雨落凡尘博客|羽落凡尘博客

标签: none

添加新评论

  • 上一篇:
  • 下一篇: