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跳过校验,但是该做法会引入中间人攻击风险。本文区分场景,分析根因,给出生产环境安全可行的全套解决方案,覆盖开发调试、内网自签证书、服务器证书缺失、系统根证书异常等场景。

问题现象
同一套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之后请求可以成功,但是控制台大量告警,并且生产环境会有安全隐患。
复现步骤
- Linux服务器系统缺少根证书包,或者Python使用的证书链损坏。
- 目标服务使用内部自签发SSL证书,不是公开CA证书。
- Python虚拟环境,内置的certifi证书库损坏、版本老旧。
- 系统时间错误,时间偏移导致证书有效期校验失败。
内网环境存在代理,代理替换SSL证书。
根因分析
- requests库默认开启SSL证书校验,需要本地存在可信根证书,用来验证远端服务器证书合法性。
- requests默认使用
certifi包提供的根证书。如果certifi版本老旧、文件损坏,就无法识别新的CA证书。 - 部分Linux极简系统(docker镜像、最小化服务器)没有安装系统ca‑certificates系统证书包。
- 目标接口使用企业内部自签证书,公开根证书库里不存在对应的签发机构,校验直接失败。
- 服务器系统时间不正确,证书不在有效期,校验失败。
设置
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参数。
- 将自签证书保存为
myca.pem,放到项目目录。 - 请求的时候指定证书文件路径:
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,不要把调试代码提交到生产。
排查调试手段
- 使用系统curl测试接口,确认是否服务器层面证书问题。
curl -v https://api.demo.com
curl同样报证书错误,说明系统层面证书、时间问题,不是Python代码问题。curl访问正常,说明Python虚拟环境certifi证书库异常。
- 打印certifi证书路径,检查文件是否存在。
确认Python是否使用虚拟环境,pip包是否和预期一致。
踩坑总结
- 遇到SSL证书报错,不要第一时间复制网上
verify=False的代码,这只是掩盖问题,留下安全漏洞。 - requests的证书来源优先certifi包,不是系统证书,虚拟环境中很容易出现证书缺失。
- 内网自签服务,把CA证书传入verify参数,保留SSL校验能力。
- docker精简镜像经常缺失ca‑certificates包,部署Python服务务必注意。
- 系统时间异常也会抛出证书校验失败,容易被忽略。
凡尘版权
友情链接:凡尘博客
凡尘博客文章|凡尘博客文摘
凡尘影院
凡尘乡音|凡尘街坊
凡尘博客|雨落凡尘博客|羽落凡尘博客
凡尘博客|雨落凡尘博客|羽落凡尘博客