Following system colour scheme - Python 增强提案 Selected dark colour scheme - Python 增强提案 Selected light colour scheme - Python 增强提案

Python 增强提案

PEP 466 – Python 2.7.x 的网络安全增强

作者:
Alyssa Coghlan <ncoghlan at gmail.com>
状态:
最终版
类型:
标准跟踪
创建日期:
2014年3月23日
Python 版本:
2.7.9
发布历史:
2014年3月23日、2014年3月24日、2014年3月25日、2014年3月26日、2014年4月16日
决议:
Python-Dev 消息

目录

摘要

大多数 CPython 跟踪器问题都被归类为行为错误或提议的增强功能。大多数用于修复行为错误的补丁都会应用到所有活动的维护分支。增强补丁仅限于成为下一个 Python 版本的默认分支。

这种节奏在 Python 通常的 18-24 个月的特性发布周期中运行良好,这仍然适用于 Python 3 系列。然而,Python 2 中标准库的年龄现在已经达到一个点,即它在网络安全协议方面已经远远落后于最新技术,这导致在短期内无法升级到 Python 3 的使用场景中出现实际问题。

鉴于 Python 2.7 在 4 年多的维护周期中出现的额外实际考虑,本 PEP 允许将一套关键的网络安全相关功能从 Python 3.4 回溯到即将发布的 Python 2.7.x 维护版本。

虽然本 PEP 没有对核心开发团队处理不再处于活跃维护状态的仅安全修复分支的方式进行任何更改,但它**确实**建议提供 Python 标准库扩展支持期的商业再分发商要么将这些功能回溯到他们支持的版本,要么明确声明不支持在涉及直接连接到公共互联网的角色中使用旧版本。

实施状态

本 PEP 最初提议将所有列出的功能添加到 Python 2.7.7 维护版本中。考虑到 PEP 最初创建和接受与 Python 2.7.7rc1 发布之间的有限时间,这种方法被证明过于雄心勃勃。相反,每个已接受的功能回溯的进展都被视为针对 Python 2.7 的独立增强功能进行跟踪。

已为 Python 2.7.7 实施

已为 Python 2.7.8 实施

已为 Python 2.7.9 实施 (开发中)

向后兼容性考虑

与 Python 3 系列一样,回溯的 ssl.create_default_context() API 被授予向后兼容性豁免,允许在维护版本中更新所创建 SSL 上下文的协议、选项、密码和其他设置,以使用更高的默认安全设置。这使得它们能够在维护发布时(而不是原始功能发布时)适当平衡兼容性和安全性。

本 PEP **不**授予对维护版本通常的向后兼容性策略的任何其他豁免。相反,通过明确鼓励使用基于功能的检查,它旨在使编写更安全的跨版本兼容 Python 软件变得更容易,同时仍然限制在升级到新的 Python 2.7 维护版本时破坏当前正在运行的软件的风险。

在所有此提案允许将新功能回溯到 Python 2.7 发布系列的情况下,可以编写通过“功能检测”(例如,检查模块中的特定属性)来操作的跨版本兼容代码,而无需显式检查 Python 版本。

然后由库和框架代码在发现缺少所需功能时提供适当的警告和回退行为。虽然一些特别注重安全性的软件在所需安全功能不可用时可能会直接失败,但大多数软件**应该**发出警告并继续使用稍微降级的安全配置运行。

回溯的 API 允许库和应用程序代码在检测到相关网络安全功能存在后执行以下操作

  • 显式选择更安全的设置(允许在具有较不安全默认行为的较旧 Python 维护版本中使用增强安全功能)
  • 显式选择较不安全的设置(允许在较低安全环境中使用较新的 Python 功能版本)
  • 确定功能的默认设置(这可能需要显式 Python 版本检查来确定 Python 功能版本,但**不需要**检查特定的维护版本)

其他模块(如更高级别的网络库和数据格式处理库)的安全相关更改将继续作为 Python 包索引上的回溯和新模块提供,因为独立分发仍然是处理必须独立于 Python 2 标准库不断发展以应对不断变化的开发需求的软件的首选方法。有关使安全网络基础设施值得特别考虑的特性,请参阅动机与原理部分。

OpenSSL 兼容性

根据此提案,OpenSSL 可能会在 Python 2.7 维护版本中升级到更新的功能版本。在 Linux 和大多数其他 POSIX 系统上,所使用的 OpenSSL 特定版本已经有所不同,因为 CPython 默认动态链接到系统提供的 OpenSSL 库。

对于 Windows 二进制安装程序,_ssl_hashlib 模块与 OpenSSL 静态链接,并且相关符号未导出。Marc-Andre Lemburg 表示,在 egenix-pyopenssl 二进制文件中更新到较新的 OpenSSL 版本尚未导致任何报告的兼容性问题[3]

Mac OS X 二进制安装程序历史上遵循与其他 POSIX 安装相同的策略,并动态链接到 Apple 提供的 OpenSSL 库。然而,Apple 现在已停止更新这些跨平台库,而是要求即使是跨平台开发人员也采用 Mac OS X 特定接口来访问其平台上最新的安全基础设施。因此,独立于本 PEP,Mac OS X 二进制安装程序已经计划切换到静态链接较新版本的 OpenSSL[4]

其他考虑

可维护性

包括 Alex Gaynor 和 Donald Stufft 在内的许多开发人员都表示有兴趣执行此策略涵盖的功能回溯,并协助解决 Python 2 系列因此产生的任何额外维护负担。

Steve Dower 和 Brian Curtin 已提出帮助创建 Windows 安装程序,让 Martin von Löwis 有机会退出维护 2.7 Windows 安装程序的工作。

本 PEP 主要旨在建立必要的共识,以允许他们开展这项工作。对于其他核心开发人员,此策略更改不应施加任何额外的工作,除了可能为那些对受影响模块特别感兴趣的开发人员审查生成的补丁。

安全发布

本 PEP 不提议对安全发布的处理进行任何更改——这些将继续是仅包含关键安全修复的纯源代码发布。

然而,对库和应用程序开发人员的建议是专门设计的,以适应选择将这些更改应用于处于仅安全修复模式或已被核心开发团队声明“生命周期结束”的其他 Python 发布系列的商业再分发商。

再分发商是否选择行使该选项将由各个再分发商决定。

集成测试

第三方集成测试服务应向用户提供针对多个 Python 2.7 维护版本(至少 2.7.6 和 2.7.7+)进行测试的能力,以确保库、框架和应用程序仍能正确测试其对传统安全基础设施的处理(根据软件的安全敏感性,要么失败,要么优雅地降级),即使在此提案涵盖的功能已回溯到 Python 2.7 系列之后。

处理风险承受能力较低的低安全环境

无论好坏(大多数是坏的),在某些环境中,对潜在安全缺陷的容忍度高于即使维护版本中回归风险略有增加。本提案在涉及豁免涵盖的模块时,很大程度上将这些环境排除在外——这种方法完全不适用于连接到公共互联网的软件,并且深度防御安全原则表明它也不适用于大多数私有网络。

下游再分发商仍可能选择迎合此类环境,但他们需要自行处理降级安全相关模块并进行相关回归测试的过程。CPython 主持续集成基础设施将不涵盖此场景。

动机与原理

本 PEP 的创建主要是由 Python 2 系列中老化的 SSL 支持所促使的。截至 2014 年 3 月,Python 2.7 SSL 模块已接近四年,而仍在流行的 Python 2.6 版本中的 SSL 支持在六年前就已锁定其功能集。

这些都太旧了,无法为通过公共互联网运行的安全网络软件提供一个可以凭良心推荐的基础,尤其是在高级持续安全威胁比以前理解的更广泛、更不加区分地针对目标的时代。虽然它们在当时代表了合理的安全基础设施,但最新技术已经发展,我们需要研究有效的机制,为那些无论出于何种原因目前无法迁移到 Python 3 的用户提供更先进的网络安全基础设施。

虽然使用系统 OpenSSL 安装解决了 Linux 平台上许多这些问题,但它并未解决所有问题(特别是,软件仍然难以明确要求一些更高级别的安全设置)。可以通过使用 PyOpenSSL 或 Pycurl 等第三方库来绕过标准库支持,但这仍然会导致安全问题,因为这些依赖项可能难以部署,并且许多用户仍然不知道他们可能需要它们。与其向可能不熟悉的用户解释如何获取和使用这些库,不如直接修复随附的“电池”。

在 python.org 上发布的 Windows 和 Mac OS X 二进制安装程序的情况下,所使用的 OpenSSL 版本完全在 Python 核心开发团队的控制之下,但目前仅限于最初随相应 Python 功能版本一起发布的 OpenSSL 维护版本。

随着受欢迎程度的提高,责任也随之增加,本提案旨在承认 Python 的受欢迎程度和采用率已达到足够高的水平,以至于我们的一些设计和政策决策对 Python 开发社区之外产生了重大影响。

例如,Python 2 ssl 模块不支持服务器名称指示标准。虽然可以通过使用第三方 requests 客户端库来获得 SNI 支持,但实际上这样做目前不仅需要使用 requests 及其嵌入式依赖项,还需要大约六个或更多额外库。因此,Python 2 系列中缺乏支持阻碍了 SNI 在服务器上的有效使用,因为 Python 2 客户端经常无法正确处理它。

另一个更关键的例子是 Python 2 标准库中缺少 SSL 主机名匹配——目前需要依赖第三方库,例如 requestsbackports.ssl_match_hostname 才能在 Python 2 中获得该功能。

Python 2 系列也比 Python 3 系列更容易受到对安全敏感比较的远程计时攻击,因为它缺少与抗计时攻击的 hmac.compare_digest() 函数等效的标准库。虽然适当的安全比较函数可以在第三方扩展中实现,但许多用户甚至不考虑这个问题,而是使用普通的相等比较——虽然标准库解决方案不能自动解决这个问题,但一旦指出问题,它**确实**大大降低了解决的障碍。

Python 2.7 代表了核心开发团队提供的唯一长期维护版本,自然地,在历史上较短的维护生命周期中有效的东西在较长的支持期内将不再有效。在本 PEP 中描述的问题的特定情况下,最简单的可用解决方案是承认网络安全相关模块的长期维护**需要**添加新功能的能力,即使要保持现有接口的向后兼容性。

对于熟悉它的人来说,值得将本 PEP 中描述的方法与 Red Hat 处理其长期开源支持承诺的方法进行比较:获得 10 年支持的不是 RHEL 6.0 版本本身,而是整个 RHEL 6 **系列**。系列中的各个 RHEL 6.x 点版本会收到各种新功能,包括安全增强功能,同时满足现有软件严格的向后兼容性保证。本 PEP 涵盖的提案使我们的长期维护方法更符合这一先例——我们保留了严格的向后兼容性要求,但对添加新功能的限制做了例外。

迄今为止,下游再分发商一直尊重我们“Python 维护版本中不添加新功能”的上游策略。本 PEP 明确接受在网络安全相关功能的情况下,更细致的策略是合适的,并且它描述的特定更改是特意设计的,使其可能适用于 Red Hat Enterprise Linux 及其下游衍生产品。

为什么选择这些特定的更改?

考虑纳入本提案的功能的关键要求是它必须对**超出**用 Python 编写的特定应用程序和该应用程序运行的系统之外的安全性产生影响。因此,重点放在网络安全协议、密码存储和相关加密基础设施——Python 是开发 Web 服务和客户端的流行选择,因此广泛使用的 Python 版本的特性对其他服务(这些服务本身可能使用较新版本的 Python 或其他开发语言)的安全设计产生影响,但需要与使用较旧版本 Python 编写的客户端或服务器进行互操作。

此要求的意图是最大限度地减少此策略的引入可能对维护版本的稳定性和兼容性产生的影响,同时仍然解决与 Python 2.7 特定方面相关的一些关键安全问题。如果最终用户对更新到新的 Python 2.7 维护版本的谨慎程度与更新到同一发布系列中的新功能版本一样,那将是彻底的适得其反。

本提案中包含 ssl 模块的更改,旨在使 Python 2 系列与过去 4 年的网络安全标准演进保持同步,并使这些标准更容易在服务器和客户端中广泛采用。类似地,hashlib 中的哈希算法可用性指示符包含在内,以使应用程序更容易在 Python 2 和 3 中检测和使用适当的哈希定义。

包含 hmac.compare_digest()hashlib.pbkdf2_hmac() 旨在帮助降低 Python 2 服务器应用程序中安全密码存储和检查的障碍。

本提案中已包含 os.urandom() 更改,以进一步鼓励用户将为加密用例提供高质量随机数的工作留给操作系统供应商。使用随机数不足可能会危及**任何**加密系统,并且操作系统开发人员拥有比典型的 Python 应用程序运行时更多的工具来充分解决该问题。

被拒绝的替代方案:仅建议开发人员迁移到 Python 3

这种替代方案代表了现状。不幸的是,它在实践中被证明是不可行的,因为向后兼容性影响意味着对于大型应用程序和集成项目来说,这是一个非平凡的迁移过程。虽然迁移工具已经发展到可以通过更新代码以在 Python 2 和 Python 3 的大公共子集中运行(而不是一次性)来机会性地增量迁移大型应用程序的程度,但在商业环境中,使用最新技术通常不是优先事项。

此前,这被认为是一种可接受的损害,因为虽然这对于受影响的开发人员来说是一个不幸的问题,但它被视为他们与管理层之间的问题,需要争取基础设施现代化,并且随着 Python 3 系列的发展,这个理由将自然变得更具说服力。

然而,现在我们完全意识到 Python 2 标准库的局限性可能对互联网安全标准演进产生的影响,我不再认为期望平台和应用程序开发人员仅仅为了获得 Python 3 中已有的网络安全增强功能而解决应用程序 Unicode 正确性中的所有潜在缺陷是合理的。

虽然 Ubuntu(以及某种程度上 Debian 也)致力于将所有默认系统服务和脚本移植到 Python 3,并从其默认分发镜像中移除 Python 2(但不是从其存档中),但这是一项艰巨的任务,并且不会在 Ubuntu 14.04 LTS 版本中完成(至少对于桌面镜像——移动和服务器镜像可能实现)。

Fedora 有更多的工作要做以进行迁移,并且迁移相关基础设施组件将需要大量时间。虽然 Red Hat 也在积极努力使用户更容易在我们稳定的平台上使用更新的 Python 版本,但这些努力开始对最终用户的版本选择产生影响需要时间,并且任何此类更改也不会惠及集成系统 Python 中运行的核心平台基础设施。

OpenStack 迁移到 Python 3 也仍处于起步阶段,即使这是一个拥有广泛且相对健壮的自动化测试套件的项目,它仍然足够大,需要相当长的时间才能完全迁移到 Python 2/3 兼容代码库。

而这仅仅是三个大量使用 Python 的最知名的开源项目。考虑到可能存在大量缺乏支持从 Python 2 迁移到 Python 3 所需的自动化回归测试套件的遗留代码,很可能在许多情况下,重新实现(甚至可能在 Python 3 中)比迁移更容易。本 PEP 的关键点是这些情况影响的人不仅仅是受影响应用程序的开发人员和用户:存在具有过时网络安全基础设施的客户端和服务器成为安全网络服务开发人员在安全设计中需要考虑的问题,而这阻碍了更好的安全标准的采用。

正如 Terry Reedy 所指出的,如果我们试图坚持现状,可能的结果是商业再分发商**仍然会**代表其客户尝试做类似的事情,但可能以不一致和随意的方式。通过将范围定义过程引入上游项目,我们能够更好地影响解决情况的方法,并有助于确保再分发商之间的一致性。

问题是真实的,所以**需要**改变,本 PEP 描述了我解决这种情况的首选方法。

被拒绝的替代方案:创建并发布 Python 2.8

如果获得足够的公司支持,**确实**有可能创建并发布 Python 2.8(此类项目极不可能获得足够多的兴趣以仅靠志愿者实现)。然而,这实际上并不能解决问题,因为目的是提供一种**相对低影响**的方式,将增强的安全功能整合到利用 Python 2 的集成产品和部署中。

升级到新的 Python 功能版本将意味着核心开发团队的工作量增加,以及更具破坏性的更新,大多数潜在最终用户可能会完全跳过。

尝试创建 Python 2.8 版本还将带来从 Python 3 回溯许多额外功能的建议(例如 tracemalloc 和改进的协程支持),这使得从 Python 2.7 迁移到这个假设的 2.8 版本更加危险和更具破坏性。

这不是推荐的方法,因为它将涉及大量的额外工作,而结果在实现原始目标(消除 Python 2 系列中当前广泛使用的老旧网络安全基础设施)方面实际上效果较差。

此外,虽然我不能承诺真正在 Red Hat 平台上解决这个问题,但我**可以**明确排除 Python 2.8 对我试图解决这个问题有任何用处的想法。

被拒绝的替代方案:通过 PyPI 分发安全增强功能

虽然这最初看起来是一种吸引人且更易于管理的方法,但它实际上存在几个严重问题。

首先,这是复杂的、低级的、跨平台代码,它与各种 POSIX 平台(包括 Mac OS X)和 Windows 上的底层操作系统集成。CPython BuildBot 机群已经设置好处理这种上下文下的持续集成,但大多数免费的持续集成服务只提供 Linux,可能还有付费访问 Windows。这些服务对于主要运行在 Python 和其他动态语言提供的抽象层以及 JVM 提供的更全面抽象之上的软件来说运行良好,但不足以满足这里涉及的代码类型。

网络安全支持的 OpenSSL 依赖项也属于“复杂二进制依赖项”,pip 基于的软件分发生态系统尚未很好地处理这类依赖项。依赖第三方二进制依赖项还会为在 PyPy 等其他解释器上运行时 pip 带来潜在的兼容性问题。

这个想法的另一个实际问题是 pip 本身依赖于标准库中的 ssl 支持(并获得捆绑的 requests 副本的一些额外支持,而 requests 又捆绑了 backport.ssl_match_hostname),因此需要任何替代模块也捆绑在 pip 中。这不会造成任何不可克服的困难(只是另一个要提供的依赖项),但它**确实**意味着又一个需要保持更新的 OpenSSL 副本。

这种方法还存在所有其他“通过重命名来提高安全性”方法相同的缺陷:它们完全错过了最需要帮助的用户,并大大阻碍了在基础设施支持时鼓励用户做正确事情的能力(因为“使用这个其他模块”的影响远高于“开启这个更高安全设置”)。废弃标准库中老旧的 SSL 基础设施而支持外部模块,将比接受与原地升级相关的轻微回归风险更具用户敌意。

最后,但肯定不是最不重要的是,这种方法与创建 Python 2.8 版本的想法存在相同的问题:可能无法解决实际问题。Python 的商业再分发商旨在再分发 **Python** 和一组预先存在的附加包。将新包添加到预先存在的集合**可以**做到,但这需要逐一联系每个再分发商,并要求他们相应地更新其重新打包过程。相比之下,本 PEP 中描述的方法将要求再分发商通过故意降级所提供的网络安全基础设施来故意**选择退出**安全增强功能,而他们中的大多数不太可能这样做。

被拒绝的变体:提供“传统 SSL 基础设施”分支

本 PEP 的早期版本包括了 2.7-legacy-ssl 分支的概念,该分支保留了 Python 2.7.6 网络安全基础设施的确切功能集。

在我看来,任何实际想要这样做的人几乎肯定犯了一个错误,如果他们坚持在特定情况下确实想要这样做,他们可以自己制作,或者安排下游分销商为他们制作。

如果它们公开发布,任何此类重建都应被称为“带有传统 SSL 的 Python 2.7”,以与包含最新网络安全基础设施的官方 Python 2.7 版本明确区分。

在本 PEP 实施的第一个 Python 2.7 维护版本之后,也将 Python 2.7.6 及更早的版本称为“带有传统 SSL 的 Python 2.7”。

被拒绝的变体:将特定模块与 Python 3 完全同步

本 PEP 的早期版本建议将 hmachashlibssl 模块与其 Python 3 对应模块完全同步。

这种方法被证明过于模糊,无法为例外情况建立令人信服的理由,因此已被当前更明确的提案取代。

被拒绝的变体:开放式回溯策略

本 PEP 的早期版本建议对未来影响互联网整体安全的 Python 3 增强功能进行一般性策略更改。

该方法造成了不必要的S不确定性,因此已简化为提出回溯一组具体的特定更改。未来的功能回溯提案可以参考本 PEP 作为先例,但仍需为 Python 2.7 长期支持版本中的每个功能添加提出具体案例。

利益披露

本 PEP 的作者目前在 Red Hat 从事测试自动化工具工作。如果此提案获得接受,我将强烈鼓励 Red Hat 利用由此产生的机会来帮助改善 Python 生态系统的整体安全性。但是,在此事中我不能代表 Red Hat 发言,也无法代表 Red Hat 做出任何承诺。

致谢

感谢 Christian Heimes 和其他人在 Python 3 系列中为大幅改进 Python 的 SSL 支持所做的努力,以及 Python 社区的众多成员帮助我更好地理解我们在 SSL 模块中提供的默认设置的影响,以及容忍使用 2010 年(Python 2.7)甚至 2008 年(Python 2.6)定义的 SSL 基础设施可能对整个 Web 安全产生的影响。

感谢 Donald Stufft 和 Alex Gaynor 确定了一组更有限的基本安全功能,使得提案比从 Python 3.4 回溯整个模块更细粒度 ([7], [8])。

Christian 和 Donald 也为本提案的初步草案提供了宝贵的反馈。

还要感谢 python-dev 邮件列表线程的参与者([1], [2], [5], [6]),以及我在 2014 年蒙特利尔 PyCon 上与我讨论此问题的各位人士。

参考资料


来源: https://github.com/python/peps/blob/main/peps/pep-0466.rst

最后修改: 2025-02-01 08:59:27 GMT