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-03-23
Python 版本:
2.7.9
历史记录:
2014-03-23, 2014-03-24, 2014-03-25, 2014-03-26, 2014-04-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 模块不支持 Server Name Indication 标准。虽然可以使用第三方 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 处理其长期开源支持承诺的方法进行比较是值得的:不是 RHEL 6.0 版本本身获得了 10 年的支持,而是整个 RHEL 6 系列。该系列中的各个 RHEL 6.x 点版本然后会获得各种新功能,包括安全增强,同时满足对现有软件的严格向后兼容性保证。本 PEP 中涵盖的提案使我们对长期维护的方法更符合这一先例 - 我们保留严格的向后兼容性要求,但对禁止添加新功能的限制做出例外。

迄今为止,下游重新分发者一直尊重我们上游的“维护版本中不添加新功能”的策略。本 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 基于的软件分发生态系统很好地处理。依赖第三方二进制依赖项也会为 pip 在其他解释器(如 PyPy)上运行时造成潜在的兼容性问题。

这个想法的另一个实际问题是 pip 本身依赖于标准库中的 ssl 支持(以及捆绑的 requests 副本提供的一些额外支持,该副本又捆绑了 backport.ssl_match_hostname),因此需要任何替换模块也捆绑在 pip 中。这不会造成任何难以克服的困难(这只是另一个需要进行供应商管理的依赖项),但这 *意味着* 又要维护 OpenSSL 的另一个副本。

这种方法也存在着所有其他“通过重命名事物来提高安全性”方法的相同缺陷:它们完全忽略了最需要帮助的用户,并且在基础设施支持的情况下,对鼓励用户做正确的事情设置了重大障碍(因为“使用另一个模块”比“打开这个更高安全性的设置”的影响大得多)。弃用标准库中陈旧的 SSL 基础设施,转而使用外部模块,将比接受在原地升级带来的略微增加的回归风险更加不利于用户。

最后,但同样重要的是,这种方法也存在着与进行 Python 2.8 版本发布的想法相同的缺陷:很可能无法解决实际问题。Python 的商业重新分发者被设置为重新分发 *Python* 和一组预先存在的附加包。将新包添加到预先存在的集合中 *可以* 完成,但这意味着要联系每个重新分发者并要求他们相应地更新他们的重新打包过程。相比之下,这个 PEP 中描述的方法将要求重新分发者通过故意降低提供的网络安全基础设施来故意 *退出* 安全增强,而大多数重新分发者不太可能这样做。

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

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

在我看来,任何真正想要这个的人几乎肯定犯了一个错误,如果他们坚持说他们真的在他们特定的情况下需要它,他们可以自己创建它,或者安排下游重新分发者为他们创建它。

如果这些重建版本被公开发布,任何这样的重建版本都应该被称为“具有 Legacy SSL 的 Python 2.7”,以清楚地将其与包含更最新网络安全基础设施的官方 Python 2.7 版本区分开来。

在第一个实现这个 PEP 的 Python 2.7 维护版本发布之后,将 Python 2.7.6 及更早版本称为“具有 Legacy SSL 的 Python 2.7”也是合适的。

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

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

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

被拒绝的变体:开放式的反向移植策略

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

这种方法造成了不必要的不明确性,因此它已经被简化为建议回移植一组特定的具体变更。未来的功能回移植建议可以参考这个 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 基础设施的使用对整个网络安全可能造成的影响。

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

Christian 和 Donald 还对该提案的初步草案提供了宝贵的反馈。

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

参考文献


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

最后修改时间:2023-10-11 12:05:51 GMT