
【免费下载链接】publicationsPublications from Trail of Bits项目地址https://gitcode.com/GitHub_Trending/pu/publications点击查看免费下载导读本文基于 Trail of Bits 安全工程师 William Woodruff 在 HushCon West 2022 上的演讲Python Packaging Mystery Meat对应仓库文档 presentations/Python Packaging Mystery Meat/README.md 及其配套幻灯片 slides.pdf系统梳理 Python 打包生态中那些看起来简单、实际上充满暗坑的设计包名与模块名脱节、sdist 文件名解析歧义、setup.py任意代码执行、wheel 与 abi3 稳定 ABI 的误用等。读完本文你将理解 Python 包在工程直觉与真实机制之间的落差掌握 sdist/wheel 两种发行格式的本质区别与风险边界并了解 Trail of Bits 为加固该生态推出的 pip-audit、abi3audit、sigstore-python 以及对 Trusted Publishing可信发布的贡献。一、工程师眼中的 Python 包一个过于美好的幻觉大多数工程师对一个 Python 包长什么样的第一印象来源于项目目录里的一次性摆设my-package/ setup.py my_package/ __init__.py some_mod.py another/ __init__.py junk.py这套心智模型幻灯片第 4 页认为包 一个带setup.py的目录树。它直观、好用但几乎每一步都与 PyPI 的真实机制不符。二、Python 包到底是什么一个仅仅的名字演讲的第二个关键论断幻灯片第 5–6 页是a python package is just a name在 PyPI 语境下包首先是索引里的一个名称project name这个名称与你在代码里import的模块名没有任何绑定关系且遵循另一套命名规则。幻灯片给出的经典例子$ python -m pip install Pillow Collecting Pillow Downloading Pillow-9.3.0-cp310-cp310-macosx_10_10_x86_64.whl (3.3 MB) Installing collected packages: Pillow Successfully installed Pillow-9.3.0 $ python import PIL PIL.__version__ 9.3.0安装的是名为Pillow的发行版导入的却是PIL模块——二者名称毫不相干。这意味着包名 模块名的直觉并不成立安全分析、依赖审计、供应链溯源时都必须区分索引名与导入路径这两层概念。三、版本与发行版包的三层结构继续沿着幻灯片第 7 页的递进逻辑一个 Python 包由多个版本versions构成每个版本又由多个发行版distributions构成。也就是说Pillow不是单个实体而是多个版本9.2.0、9.3.0、10.0.0……每个版本对应一个或多个发行版文件distribution artifacts供不同平台/解释器下载。PyPI 官方支持两种发行格式幻灯片第 8 页发行格式全称每个版本的数量特点sdistsource distribution源码发行版每个版本一个未构建的原始形态wheelbuilt distribution构建后发行版每个版本多个结构化元数据与布局四、sdist打包领域的先试错再找答案幻灯片第 9–10 页将 sdist 直白地称为the fuck around and find out approach to packaging打包领域踩了坑才知道的做法因为它在文件名这一层就埋着歧义。4.1 文件名解析歧义cffi-1.0.2-2.tar.gz在 PEP 625 之前sdist 的文件名只有一条松散的约定{project}-{version}.tar.gz。问题在于version是 PEP 440 版本允许包含连字符如1.0.2-2而project名称按 PEP 345 及后续规范同样允许包含连字符。于是同一个文件名可以有两种完全不同的切分方式幻灯片原例cffi-1.0.2-2.tar.gz → Package(cffi), Version(1.0.2-2) → Package(cffi-1-0-2), Version(2)结论是PyPI 上存在一些包仅凭 sdist 的索引条目根本无法无歧义地解析出名称与版本。pip等工具只能靠额外维护解析上下文parse context来打补丁式绕过。该问题由PEP 625规定 sdist 文件名规范修复演讲时该 PEP 刚被接受约两个月但正如演讲所指出的由于大量历史发行版的存在这些 hack 仍会长期保留——这正是打包黑箱的一个典型缩影。4.2 setup.py一个可以做任何事的构建脚本幻灯片第 11 页给出 sdist 的实质sdists are just a tarball of the Python source plus a build/install script——that script is usually setup.py即sdist 本质上是Python 源码 构建/安装脚本的压缩包这个脚本通常是setup.py。而幻灯片第 12 页点明了最危险的事实setup.py can do anything it wants! including hooking subcommands!setup.py是任意可执行 Python 代码它可以做任何事——包括钩住hookingpip的子命令。这意味着仅仅解析元数据这一操作就可能触发任意代码执行ACE, Arbitrary Code Execution。4.3 那些你以为安全的命令其实都会执行 setup.py演讲用两个反直觉的案例幻灯片第 15–17 页说明问题的严重性pip install --dry-run——是 ACE 载体吗直觉上试运行不应该执行构建脚本但事实并非如此简单因为 pip 在解析依赖、生成元数据时仍可能触碰setup.pypip download——是 ACE 载体吗演讲给出的答案明确pip download会调用setup.py egg_info来生成包元数据也就是说即便你只是想下载一个包、并不安装也可能在执行对方代码。幻灯片原例$ pip download --no-deps issue7325该例对应历史上与pip download触发源码构建相关的 issue用于演示下载也会执行代码这一事实。结合幻灯片第 13–14 页引用的 Sonatype 相关恶意包研究可以得出清晰的安全结论凡是以 sdist 形式分发的包任何触及元数据生成的 pip 操作都存在远程代码执行面——这正是 sdist 被称为mystery meat来路不明的肉的原因之一。五、wheel结构化布局而非二进制的魔法幻灯片第 18–20 页转向built distributions并首先澄清一个常见误解built doesnt actually mean binary code; it means structured metadata and layout, rather than a YOLOd tarball即wheel 的已构建指的并不是二进制代码而是结构化的元数据与文件布局——它替代了那种随缘打包YOLOd的 tarball。由此得到两个重要推论wheel 可以是纯 Python 的。对于纯 Python 包2022 年乃至今天的正确分发方式就是 wheel不存在setup.py的任意代码执行路径。构建 wheel 的工具链已经成熟python -m build应该开箱即用幻灯片原话 tooling for wheels is mature; python -m build should just work。六、二进制 wheel好处与代价6.1 wheel 里可以装什么幻灯片第 21 页指出wheel 同样可以包含二进制内容Python 扩展模块对应的可加载共享对象打包进 wheel 的原生依赖的共享对象。import foo会搜索一串路径其中包括foo.so、foo.abi3.so等。工具如auditwheel通过自动把依赖重定位relocate进发行版来辅助这一过程。6.2 二进制 wheel 的利弊幻灯片第 23 页给出公允的评估好处终端用户不必自己跑复杂的源码构建不会把构建搞错也不需要维护工具链支持代价维护者要面对巨大的构建/支持矩阵——约 5 个 Python 版本含 PyPy× 3 个宿主操作系统 × 每个系统 2~3 种 libc 构建且很难充分测试。工具如cibuildwheel通过自动化大规模矩阵上的 wheel 构建来缓解。6.3 不太稳定的稳定 ABIabi3CPython 提供了所谓的稳定 ABIstable ABI即 abi3为abi3-py36构建的 wheel 与 CPython 3.6 均兼容从而大幅缩减构建矩阵。但幻灯片第 24 页揭示了它的暗坑要使用稳定 ABI需要在 C 代码中定义Py_LIMITED_API并将其设为你想兼容的版本而这个宏与用来把 wheel 标记为稳定 ABI 兼容的 CLI 选项完全脱节disconnected。也就是说声明兼容与真实兼容是两套彼此无关的机制很容易出现标了 abi3 却没按 abi3 构建的错误。6.4 abi3audit给错误标记的 wheel 验尸针对上述问题Trail of Bits 开发了abi3audit工具幻灯片第 25 页专门排查被错误标记为稳定 ABI 的 wheel。扫描结果是惊人的15% of all PyPI projects have at least one mis-tagged wheel即PyPI 上约 15% 的项目至少存在一个误标mis-tagged的 wheel。演讲同时强调ABI 不匹配意味着潜在的崩溃与内存破坏crashes and memory corruption——这已不是打包美学问题而是真实的安全/稳定性风险。七、展望Trail of Bits 正在推动的四件大事幻灯片第 26 页以一张路线图收束演讲列出四大趋势这也正是仓库内多个关联主题的线索状态趋势说明✅ 已完成更难配置错包、更容易产出 wheelPEP 517pyproject.toml、pypa/build、pypa/flit✅ 已完成更容易审计依赖漏洞pypa/pip-audit 进行中无需 PGP 即可对 Python 包代码签名Sigstoresigstore-python 进行中无需管理凭据即可发布到 PyPIOIDC 联合Trusted Publishing与 GitHub Actions 等 CI 提供商集成7.1 为什么这些是加固而非修修补补结合仓库中同一作者、同一方向的后续演讲可以更完整地理解这条加固主线依赖漏洞审计pip-audit 把审计依赖是否含已知漏洞变成一条命令仓库内演讲 Automated Tools for Securing the Software Supply Chain 对该方向有系统阐述代码签名Sigstore 方案的核心是用短期密钥/证书替代长期密钥PGP 的根本缺点仓库内 Sigstore for Python Packaging: Next Steps for Adoption 与 Ergonomic codesigning for the Python ecosystem with Sigstore 分别覆盖了如何说服生态采纳与Sigstore 安全模型入门可信发布基于 OpenID Connect 的短期身份凭证取代长寿命 API TokenTrusted Publishing: Lessons from PyPI 完整回顾了其在 PyPI 上的实现与威胁模型考量端到端溯源由 PEP 740Index support for digital attestations标准化的数字 attestation把签名、透明性与 SLSA/in-toto 结合PEP 740 and PyPI: Bootstrapping Provenance for the Python Ecosystem 与 Attestations: a new generation of signatures on PyPI 详述了这一体系的架构与默认签名策略。这些工作有一个共同动机正是本演讲的核心论点Python 打包体系的结构性复杂度而不是恶意本身才是最大的攻击面——加固的方向是把隐式信任任意代码变成显式验证可审计证据。八、彩蛋值得你自行检索的被诅咒边界情况幻灯片第 27 页的 addenda 列出若干你自己去查会比较有趣的诡异细节这里原样整理并补充一句说明URL 依赖格式中的#egg片段如foo[bar] githttps://example.com/baz#eggquux[lol]——依赖指定语法里藏着额外解析规则版本号长度没有规定上限幻灯片注明 no specified limit on version length极端版本号同样带来解析与存储负担部分 wheel 用-ffast-math构建这会破坏远端代码中的浮点语义演讲引用 Brendan Dolan-Gavitt 的someones been messing with my subnormals!即二进制里混入了非标准浮点行为依赖解析不存在保证的固定点fixed point正因为setup.py可以动态输出依赖PyPI 无法可靠获知项目的完整依赖集演讲引用 Dustin Ingram 的why PyPI doesnt know your projects dependencies。九、结语与延伸阅读Python Packaging Mystery Meat的结论可以浓缩为一句话Python 的打包体系是一锅成分不明的炖肉——名字与模块脱钩、sdist 文件名有歧义、setup.py可执行任意代码、abi3 标记可能失真而 Trail of Bits 的应对不是绕开它而是用工具与标准把它变得可审计、可验证、可追溯。这条主线在该仓库中有完整的一手资料可供继续深挖本演讲幻灯片presentations/Python Packaging Mystery Meat/slides.pdf依赖漏洞审计Automated Tools for Securing the Software Supply Chain可信发布OIDCTrusted Publishing: Lessons from PyPI、Securing your Package Ecosystem with Trusted Publishing代码签名SigstoreErgonomic codesigning for the Python ecosystem with Sigstore、Sigstore for Python Packaging: Next Steps for Adoption端到端溯源PEP 740 / attestationPEP 740 and PyPI: Bootstrapping Provenance for the Python Ecosystem、Attestations: a new generation of signatures on PyPI生态远期展望The Next 5 Years of Supply Chain Security on PyPI、Imagining a zero-trust future for PyPI如果你想亲自检验这些风险最直接的方式是用pip download --no-deps 某个 sdist 包观察元数据生成阶段的行为用python -m build对比 sdist 与 wheel 的构建差异再用 abi3audit / pip-audit 扫描你正在使用的依赖——你很快会体会到mystery meat 正是对这套体系最贴切的形容。赞分享【免费下载链接】publicationsPublications from Trail of Bits项目地址https://gitcode.com/GitHub_Trending/pu/publications点击查看免费下载相关推荐Trail of Bits 演讲解读深入分析 Flame 恶意软件中的 MD5 碰撞攻击Trail of Bits 演讲解读深入分析 Flame 恶意软件中的 MD5 碰撞攻击 本篇技术指南基于本仓库 presentations/Analyzin整体化机器学习威胁建模Trail of Bits 的 Holistic ML Threat Models 方法论与实践整体化机器学习威胁建模Trail of Bits 的 Holistic ML Threat Models 方法论与实践 机器学习系统的威胁建模不能只盯着模型本Apache APISIX SSL 协议配置指南静态与动态 TLS 版本控制实战Apache APISIX SSL 协议配置指南静态与动态 TLS 版本控制实战 导读 本文讲解 Apache APISIX 中 TLS/SSL 协议版本T上一篇goERP二次开发教程如何扩展和定制你的进销存系统下一篇3D稀疏卷积引擎深度剖析Lidar_AI_Solution如何实现422MB低内存占用推理创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考