pip 的 HTTPS 证书验证完全指南:自定义 CA 证书库与系统信任存储(truststore)机制解析
2026/9/24 15:48:51 网站建设 项目流程
  • 包管理器
  • 开发工具

【免费下载链接】pip

The Python package installer

项目地址:https://gitcode.com/gh_mirrors/pi/pip
点击查看免费下载

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 适配器(HTTPAdapterCacheControlAdapter);而不在名单内的 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_BUNDLEcurl 生态约定的 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文件后,即可通过上面的--certPIP_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

几个值得注意的实现细节:

  1. certifi 仍在参与:truststore 上下文通过load_verify_locations(certifi.where())把 certifi 的 bundle 也加载进去,这就是文档所说"系统证书与 certifi 叠加使用"的直接证据;
  2. 优雅降级:如果 Python 缺少ssl模块,或当前平台不支持 truststore,pip 会记录警告并返回None,退化为纯 certifi 验证,而不是报错中断;
  3. 结果缓存@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-certsssl_contextNone,pip 走纯 certifi 的默认验证路径;否则创建 truststore 上下文。由于它被标记为--use-deprecated,可以预期该选项在未来版本中会被移除,届时系统证书验证将成为唯一路径。

证书验证的完整调用链:从参数到 TLS 握手

把上面几节串起来,一次pip install的证书验证调用链大致如下:

  1. 参数解析--cert--client-cert--use-deprecated等选项由 cmdoptions.py 定义并进入options
  2. 会话构建_build_session()(index_command.py)按legacy-certs与否创建ssl_context,随后:
    • session.verify = options.cert(用户指定自定义 CA bundle 时生效);
    • session.cert = options.client_cert(设置 TLS 客户端证书,一个包含私钥与证书的 PEM 文件);
  3. 适配器挂载PipSession(network/session.py)将ssl_context注入HTTPAdapter/CacheControlAdapter(两者继承自_SSLContextAdapterMixin),并分别挂载到https://http://file://
  4. TLS 握手:urllib3 连接池使用该ssl_context完成证书链校验。

其中_SSLContextAdapterMixin(network/session.py)有两个值得注意的细节:

  • init_poolmanagerssl_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)。

实践建议与故障排查清单

  1. 内网/私有索引使用自签 CA:优先把 CA 导入操作系统信任库并升级到 pip ≥ 24.2,即可免配置生效;无法升级时用--cert <pem路径>PIP_CERT显式指定;
  2. 希望与 curl 保持一致的证书行为:设置CURL_CA_BUNDLE(或REQUESTS_CA_BUNDLE)指向同一 bundle;
  3. 出现证书错误且已配置系统 CA:先用pip debug检查环境变量是否被意外设置、再用pip install --use-deprecated=legacy-certs对比验证是否是 truststore 路径的问题;若是,则按官方指引转向 truststore 项目反馈,而不是 pip 仓库;
  4. 需要双向 TLS(客户端证书)的索引:使用--client-cert <单文件PEM>(私钥与证书在同一文件);
  5. 明确只有 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

项目地址:https://gitcode.com/gh_mirrors/pi/pip
点击查看免费下载

相关推荐

上一篇:Windows热键冲突终极指南:3分钟定位占用快捷键的元凶
下一篇:Windows热键冲突检测终极指南:3分钟找出占用快捷键的程序

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询