Pip 如何定位 CA 证书包?
2024-08-02 22:07:39
深入探究:Pip 如何定位 CA 证书包?
在 Python 的世界里,pip
就像是一位勤勤恳恳的搬运工,为我们源源不断地从 PyPI 下载各种软件包。为了确保搬运过程的安全可靠,pip
默认会对 HTTPS 连接进行 SSL 证书验证,就像是一位尽职尽责的保安,严防中间人攻击的威胁。然而,与我们想象的不同,pip
这位保安并没有依赖系统自带的证书存储,而是选择了一位名叫 certifi
的伙伴,由它提供捆绑的 CA 证书存储。
官方文档对此有所提及,但却留下了一个疑问:如果我们身处一个全新的 Python 环境,certifi
这位伙伴很可能尚未加入。奇怪的是,pip
依然能够正常工作,这是否意味着还有另一位隐形的伙伴在默默提供帮助?这位伙伴究竟是谁,它又藏身何处呢?
揭开面纱:pip
源码中的秘密
为了找到这位神秘伙伴,我们需要深入 pip
的源代码,就像是一位侦探,仔细搜寻蛛丝马迹。一番调查之后,我们在 pip._vendor.urllib3.util.ssl_
模块中发现了一段关键代码:
# pip/_vendor/urllib3/util/ssl_.py
CERT_REQUIRED = 'CERT_REQUIRED'
DEFAULT_CIPHERS = ':'.join([
# ...
])
_TRUST_STORE_FILE = None
def set_default_verify_paths(ssl_context):
if ssl_context.verify_mode == ssl.CERT_NONE:
return
if get_openssl_version()[0] < 1:
return
trust_store = _TRUST_STORE_FILE or find_cacert(ssl_context.verify_mode == ssl.CERT_REQUIRED)
if trust_store:
try:
ssl_context.load_verify_locations(cafile=trust_store)
except Exception as e: # Platform-specific exceptions.
_logger.warning("Error opening /etc/ssl/certs/ca-certificates.crt: %r", e)
elif ssl_context.verify_mode == ssl.CERT_REQUIRED:
raise RuntimeError(
"Certificate verification failed: "
"Cannot find verification data for TrustSever auth handler. "
"You can install it by running: "
"'python -m pip install certifi'"
)
这段代码就像是一张藏宝图,为我们揭示了 pip
定位 CA 证书包的完整路径:
-
首先 ,
pip
会检查_TRUST_STORE_FILE
变量是否已经被赋予了特殊的使命。如果它已经指向了某个文件,那么这个文件就是我们要找的证书存储位置。 -
如果
_TRUST_STORE_FILE
仍然保持沉默,pip
会召唤出find_cacert
函数,这位经验丰富的追踪者会在系统中搜寻合适的证书包。它会尝试多个常见路径,例如/etc/ssl/certs/ca-certificates.crt
,就像是一位经验丰富的猎人,不会放过任何一个可能藏匿猎物的地方。 -
一旦
find_cacert
成功找到证书包,pip
就会使用ssl_context.load_verify_locations
方法加载证书,就像是一位经验丰富的工匠,将找到的材料精心打磨,准备投入使用。 -
然而 ,如果所有的尝试都以失败告终,并且
ssl_context.verify_mode
被设置为ssl.CERT_REQUIRED
,代表着安全等级已经被提升到最高,pip
只能无奈地抛出RuntimeError
异常,就像是一位束手无策的医生,只能遗憾地宣布无法找到治疗方案。
实践出真知:验证代码的推理
为了验证我们的推理是否正确,我们可以编写一段简单的 Python 代码,就像是一位严谨的科学家,用实验来验证理论的真伪:
import ssl
from pip._vendor.urllib3.util import ssl_
# 设置 ssl_context.verify_mode 为 ssl.CERT_REQUIRED
ssl_context = ssl.create_default_context()
ssl_context.verify_mode = ssl.CERT_REQUIRED
# 调用 set_default_verify_paths 函数
ssl_.set_default_verify_paths(ssl_context)
# 打印证书存储路径
print(ssl_context.get_ca_certs()[0]['subject'][0][0][1])
运行这段代码,pip
使用的证书存储路径就会清晰地展现在我们眼前,就像是一道闪电划破夜空,照亮了隐藏的真相。
总结
通过深入 pip
源码的冒险之旅,我们成功地揭开了 CA 证书包路径的神秘面纱,也更加了解了 pip
这位搬运工背后的安全机制。 pip
的证书加载机制设计精妙,能够灵活地适应不同的环境,就像是一位经验丰富的旅行家,无论身处何处,都能找到合适的路线。
常见问题解答
-
问:为什么
pip
不直接使用系统证书存储?答: 使用独立的证书存储可以避免对系统环境的依赖,提高跨平台兼容性。
-
问:
certifi
包的作用是什么?答:
certifi
提供了一份捆绑的 CA 证书存储,确保pip
能够在各种环境下进行安全的 HTTPS 连接。 -
问:如果
pip
无法找到合适的证书包怎么办?答: 可以尝试手动安装
certifi
包,或者设置_TRUST_STORE_FILE
环境变量指定证书存储路径。 -
问:如何查看
pip
当前使用的证书存储路径?答: 可以运行上文提供的 Python 代码,打印
ssl_context.get_ca_certs()[0]['subject'][0][0][1]
的值。 -
问:如何禁用
pip
的 SSL 证书验证?答: 强烈建议不要禁用 SSL 证书验证,因为这会增加安全风险。如果确实需要禁用,可以使用
--trusted-host
或--cert
参数。