- 包管理器
- 开发工具
【免费下载链接】pip
The Python package installer
pip 在通过 HTTPS 下载包时默认执行 SSL 证书验证,以防范针对软件包下载的中间人攻击。本文以 docs/html/topics/https-certificates.md 为主线,系统讲解 pip 的证书验证默认行为、如何用--cert/PIP_CERT/REQUESTS_CA_BUNDLE/CURL_CA_BUNDLE指定自定义 CA 证书库,以及 pip 24.2 起默认集成系统证书库(基于 truststore)的实现细节与退出开关。读完本文,你将能够根据企业内网、私有索引、代理环境等不同场景,准确配置 pip 的证书验证策略,并能理解其底层调用链以便排查 TLS/SSL 故障。
pip 默认的证书验证行为:为什么需要它
从 pip 1.3 版本开始,pip 对其发起的每一个 HTTPS 网络连接都会执行 SSL 证书验证。这一点在官方文档中被明确强调:证书验证的目的,是防止针对软件包下载的中间人攻击(man-in-the-middle)——如果没有验证,攻击者可以在网络链路上伪装成 PyPI 或任意软件源,向你分发被篡改的包。
这一默认行为在源码中有清晰体现。在 src/pip/_internal/network/session.py 中,pip 维护了一份"安全来源"(secure origin)白名单:
SECURE_ORIGINS: list[SecureOrigin] = [ # protocol, hostname, port ("https", "*", "*"), # 任何 HTTPS 来源 ("*", "localhost", "*"), # 本机回环 ("*", "127.0.0.0/8", "*"), ("*", "::1/128", "*"), ("file", "*", None), ("ssh", "*", "*"), # ssh 始终视为安全 ]当目标来源匹配安全白名单时,pip 使用执行证书验证的 HTTPS 适配器(HTTPAdapter或CacheControlAdapter);而不在名单内的 HTTP 来源会被忽略并记录警告。这套机制同时解释了另一个常见现象:pip 对http://来源使用InsecureHTTPAdapter,其cert_verify方法强制以verify=False关闭验证(见 session.py 的 InsecureHTTPAdapter),因为明文 HTTP 本来就没有任何证书可言。
使用特定证书库:--cert 与相关环境变量
默认情况下,pip 使用随自身分发的certifi证书包(当前仓库内 vendored 版本为certifi==2026.7.22,见 src/pip/_vendor/vendor.txt)。如果你的公司或机构使用自建的 CA 签发的证书,或者你依赖某个特定的证书捆绑包,就需要覆盖这一默认行为。
--cert 命令行参数
--cert允许用户为 pip 指定不同的证书库(certificate store / bundle):
pip install --cert /path/to/my-ca-bundle.pem some-package该参数在 src/pip/_internal/cli/cmdoptions.py 中的定义如下:
cert: Callable[..., Option] = partial( PipOption, "--cert", dest="cert", type="path", metavar="path", help=( "Path to PEM-encoded CA certificate bundle. " "If provided, overrides the default. " "See 'SSL Certificate Verification' in pip documentation " "for more information." ), )要点:
- 值必须是PEM 编码的 CA 证书捆绑包文件路径(
type="path"); - 一旦提供,会覆盖默认证书库(即不再单独使用 certifi);
- 它是通用选项,属于
general_group(见 cmdoptions.py 的 general_group),因此适用于 install、download、wheel、index 等所有会访问网络的子命令; - 由于是通用选项,同样可以写入 pip 配置文件,例如
pip.conf的[global]段:
[global] cert = /etc/ssl/certs/my-company-ca.pem环境变量:PIP_CERT、REQUESTS_CA_BUNDLE、CURL_CA_BUNDLE
官方文档明确列出的环境变量包括:
| 环境变量 | 作用 |
|---|---|
PIP_CERT | --cert的环境变量形式,优先级与命令行参数一致(命令行参数会覆盖) |
REQUESTS_CA_BUNDLE | 传统 requests 生态约定的 CA bundle 环境变量,被 pip 的网络栈兼容识别 |
CURL_CA_BUNDLE | curl 生态约定的 CA bundle 环境变量,同样被兼容识别 |
一个典型的用法示例:
export PIP_CERT=/path/to/my-ca-bundle.pem pip install some-package值得一提的是,pip debug命令会在输出中列出当前环境中这两个 curl/requests 生态变量的值(见 tests/functional/test_debug.py 中对REQUESTS_CA_BUNDLE:与CURL_CA_BUNDLE:输出的断言),因此当你怀疑环境变量影响验证结果时,可先运行pip debug检查。
获取一份特定的 CA bundle
如果你需要一个现成的权威证书捆绑包,官方文档建议使用 curl 项目提供的Mozilla CA bundle(CA 证书提取自 Mozilla 的根证书库)。得到.pem文件后,即可通过上面的--cert或PIP_CERT交给 pip 使用。对于离线内网环境,这也是把 CA 证书注入 pip 的最直接方式。
使用系统证书库:truststore 与 pip 24.2 的行为变更
版本演进:从 opt-in 到默认开启
| pip 版本 | 行为 |
|---|---|
| < 22.2 | 不使用系统证书 |
| 22.2 起 | 需要显式--use-feature=truststore才使用系统证书(opt-in) |
| 24.2 起 | 默认使用系统证书,与 certifi 叠加用于验证 HTTPS 连接 |
也就是说,从 pip 24.2 开始,pip 在验证 HTTPS 连接时同时使用系统证书库与 certifi,无需任何配置。这解决了企业环境中最常见的痛点:内网代理或私有 PyPI 使用企业自签 CA 时,只要该 CA 已导入操作系统信任库(macOS 钥匙串、Windows 证书存储、Linux 的 /etc/ssl/certs 等),pip 就能直接信任,不必再手动导出证书文件。
truststore 是什么
这一功能由truststore包提供。当前仓库将truststore==0.10.4作为 vendored 依赖内置(见 src/pip/_vendor/vendor.txt 与 src/pip/_vendor/truststore/init.py)。truststore 的核心能力是让 Python 的ssl.SSLContext直接加载操作系统原生的信任存储,而不是依赖独立的 CA 文件。它要求 Python 3.10 及以上版本(且需要运行时可用的SSLObject.get_unverified_chain()相关 API)。
源码级实现:truststore 上下文是如何被创建的
在 src/pip/_internal/cli/index_command.py 中,pip 创建 truststore SSL 上下文的过程如下:
@lru_cache def _create_truststore_ssl_context() -> SSLContext | None: try: import ssl except ImportError: logger.warning("Disabling truststore since ssl support is missing") return None try: from pip._vendor import truststore except ImportError: logger.warning("Disabling truststore because platform isn't supported") return None ctx = truststore.SSLContext(ssl.PROTOCOL_TLS_CLIENT) ctx.load_verify_locations(certifi.where()) return ctx几个值得注意的实现细节:
- certifi 仍在参与:truststore 上下文通过
load_verify_locations(certifi.where())把 certifi 的 bundle 也加载进去,这就是文档所说"系统证书与 certifi 叠加使用"的直接证据; - 优雅降级:如果 Python 缺少
ssl模块,或当前平台不支持 truststore,pip 会记录警告并返回None,退化为纯 certifi 验证,而不是报错中断; - 结果缓存:
@lru_cache保证同一进程中只创建一次 SSL 上下文。
遇到 TLS/SSL 错误时该找谁
官方文档有一个明确的运维提示:当使用 truststore 功能遇到 TLS/SSL 错误时,应当向 truststore 项目的维护者反馈问题(在 truststore 的 GitHub issue 跟踪器中提交),而不是向 pip 的 issue 跟踪器提交。因为该环节的证书加载与系统信任库解析逻辑完全由 truststore 负责,其维护者才具备诊断和修复该问题所需的上下文。pip 侧的适配层(_SSLContextAdapterMixin)只是把 truststore 生成的SSLContext透传给网络适配器。
功能测试佐证
tests/functional/test_truststore.py 提供了一个pip_no_truststorefixture,它在每次调用时附加--use-deprecated=legacy-certs参数,并验证"关闭 truststore 后依然可以正常安装 PyPI 包与 GitHub 直接下载的包"。这说明 truststore 与 legacy 证书路径在功能上是等价的,只是信任来源不同。
退出系统证书:--use-deprecated=legacy-certs
如果系统证书库的引入反而造成了问题(例如系统信任库被污染、或你希望严格只信任 certifi),pip 提供了退出开关:
pip install --use-deprecated=legacy-certs some-package该标志名为legacy-certs,是--use-deprecated选项的合法取值之一。选项定义见 src/pip/_internal/cli/cmdoptions.py:
use_deprecated_feature: Callable[..., Option] = partial( Option, "--use-deprecated", dest="deprecated_features_enabled", metavar="feature", action="append", default=[], choices=[ "legacy-resolver", "legacy-certs", ], help=("Enable deprecated functionality, that will be removed in the future."), )在 index_command.py 的 _build_session 中,是否启用系统证书正是通过检查该选项决定的:
if "legacy-certs" not in options.deprecated_features_enabled: ssl_context = _create_truststore_ssl_context() else: ssl_context = None即:传了legacy-certs时ssl_context为None,pip 走纯 certifi 的默认验证路径;否则创建 truststore 上下文。由于它被标记为--use-deprecated,可以预期该选项在未来版本中会被移除,届时系统证书验证将成为唯一路径。
证书验证的完整调用链:从参数到 TLS 握手
把上面几节串起来,一次pip install的证书验证调用链大致如下:
- 参数解析:
--cert、--client-cert、--use-deprecated等选项由 cmdoptions.py 定义并进入options; - 会话构建:
_build_session()(index_command.py)按legacy-certs与否创建ssl_context,随后:session.verify = options.cert(用户指定自定义 CA bundle 时生效);session.cert = options.client_cert(设置 TLS 客户端证书,一个包含私钥与证书的 PEM 文件);
- 适配器挂载:
PipSession(network/session.py)将ssl_context注入HTTPAdapter/CacheControlAdapter(两者继承自_SSLContextAdapterMixin),并分别挂载到https://、http://、file://; - TLS 握手:urllib3 连接池使用该
ssl_context完成证书链校验。
其中_SSLContextAdapterMixin(network/session.py)有两个值得注意的细节:
init_poolmanager把ssl_context通过pool_kwargs传给连接池,实现"运行时动态决定使用哪套证书库";proxy_manager_for除了设置ssl_context,还设置了proxy_ssl_context:当通过 HTTPS 代理连接时,urllib3 对代理本身会另开一条 TLS 连接(隧道建立前),必须让这条代理连接也使用同一套(truststore)SSL 上下文,否则代理这条腿会用普通默认上下文验证,产生虚假的证书错误。这一行为有对应的单元测试保护(见 tests/unit/test_network_session.py 的 TestSSLContextAdapterMixinProxy,其中断言proxy_manager.proxy_ssl_context is ssl_context)。
实践建议与故障排查清单
- 内网/私有索引使用自签 CA:优先把 CA 导入操作系统信任库并升级到 pip ≥ 24.2,即可免配置生效;无法升级时用
--cert <pem路径>或PIP_CERT显式指定; - 希望与 curl 保持一致的证书行为:设置
CURL_CA_BUNDLE(或REQUESTS_CA_BUNDLE)指向同一 bundle; - 出现证书错误且已配置系统 CA:先用
pip debug检查环境变量是否被意外设置、再用pip install --use-deprecated=legacy-certs对比验证是否是 truststore 路径的问题;若是,则按官方指引转向 truststore 项目反馈,而不是 pip 仓库; - 需要双向 TLS(客户端证书)的索引:使用
--client-cert <单文件PEM>(私钥与证书在同一文件); - 明确只有 certifi 可信:禁用系统证书库,使用
--use-deprecated=legacy-certs。
延伸阅读
- 官方文档原文:docs/html/topics/https-certificates.md
- 证书参数与
--use-deprecated定义:src/pip/_internal/cli/cmdoptions.py - truststore SSL 上下文创建与会话构建:src/pip/_internal/cli/index_command.py
- 会话与适配器实现(含安全来源白名单、代理 SSL 上下文):src/pip/_internal/network/session.py
- truststore 与 certifi 的 vendored 版本:见 src/pip/_vendor/truststore/init.py 与 src/pip/_vendor/vendor.txt
- 相关测试:tests/functional/test_truststore.py、tests/unit/test_network_session.py、tests/functional/test_debug.py
- 包管理器
- 开发工具
【免费下载链接】pip
The Python package installer
相关推荐
curl HTTPS/TLS 证书校验全指南:验证原理、CA 信任存储管理与排障实战
curl HTTPS/TLS 证书校验全指南:验证原理、CA 信任存储管理与排障实战 这篇技术指南以 docs/SSLCERTS.md https://link
CLI网络通信DBeaver插件证书信任存储管理:维护自定义CA证书的方法
DBeaver插件证书信任存储管理:维护自定义CA证书的方法 你是否在使用DBeaver连接数据库时遇到过SSL证书验证失败的问题?特别是当数据库使用自签名证书
数据库客户端桌面应用数据库Traefik 证书配置完全指南:用户自定义证书、证书存储与默认证书(Certificates & Stores)
Traefik 证书配置完全指南:用户自定义证书、证书存储与默认证书(Certificates & Stores) 本篇技术指南围绕 Traefik(云原生应用
后端API网关负载均衡微服务网络云原生
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考