稳健性原则的有害后果

2022-02-15 15:58:19

稳健性原则,通常被称为";发送的内容要保守,接受的内容要自由";,长期以来一直指导着互联网协议的设计和实施。该声明倡导的姿态在短期内促进了互操作性,但随着时间的推移,可能会对原生态系统产生负面影响。对于积极维护的协议,可以也应该避免使用健壮性原则。¶

本文档的讨论在架构讨论邮件列表(架构)上进行[email protected]),存档于https://mailarchive.ietf.org/arch/browse/architecture-discuss/. ¶

本互联网草案完全符合BCP 78和BCP 79的规定。¶

互联网草案是互联网工程任务组(IETF)的工作文件。请注意,其他小组也可以将工作文件作为互联网草稿分发。目前互联网上的草稿列表位于https://datatracker.ietf.org/drafts/current/. ¶

互联网草案是有效期最长为六个月的文件草案,可随时由其他文件更新、替换或作废。不应将互联网草稿用作参考资料或引用as"以外的内容;工作正在进行中" ¶

版权(C)2021 IETF信托和被认定为文件作者的人。版权所有。¶

本文件受BCP 78和IETF信托'的约束;s与IETF文件相关的法律规定(https://trustee.ietf.org/license-info)自本文件出版之日起生效。请仔细阅读这些文件,因为它们描述了您对本文件的权利和限制。从本文档中提取的代码组件必须包括简化的BSD许可证文本,如第4节所述。如简化BSD许可证所述,不提供任何担保。¶

稳健性原则对互联网的设计有着巨大的影响。正如IAB RFC 1958[原则]所述,稳健原则建议:

发送时要严格,接收时要宽容。在发送到网络时,实现必须严格遵循规范,并容忍来自网络的错误输入。当有疑问时,无提示地放弃错误输入,而不返回错误消息,除非规范要求这样做。¶

这个简单的陈述抓住了互操作系统设计中的一个重要概念。许多人认为,应用RoistNeNess原则,有助于互联网的成功,以及一般的互操作协议的设计。¶

时间和经验表明,如果实现应用健壮性原则,那么对互操作性的负面影响会随着时间的推移而累积。这个问题源于一个隐含在原则中的假设,即不可能影响互联网规模的系统的变化。也就是说,一旦协议规范发布,可能需要现有实现才能更改的更改是不可行的。¶

对于处于主动维护状态的协议,许多可能导致应用健壮性原则的问题都是无效的。主动协议维护是指协议设计者、实施者和部署者共同努力,在这些协议的实施和部署过程中不断改进和发展协议规范。在协议维护中扮演积极角色的社区可以大大减少甚至消除应用健壮性原则的机会。¶

有很好的证据表明,许多重要的协议都是在其初始阶段之后常规维护的。尤其是,相当大一部分IETFactivity致力于现有协议的管理。本文件主要用于记录应用稳健性原则所固有的危害,并为处理部署中的互操作性问题提供替代策略。¶

理想情况下,协议实现永远不必应用健壮性原则。或者,在不可避免的情况下,稳健性原则的使用被视为一种需要迅速恢复的短期变通方法。¶

稳健性原则产生的背景为其意图和目的提供了有价值的见解。theRFC系列(在RFC 760[IP]中)中,原则的最早形式前面有一句话,揭示了原则的动机:

虽然本规范的目标是明确协议,但可能存在不同的解释。一般来说,实现在发送行为上应该是保守的,在接收行为上应该是自由的。¶

该原则的这种表述明确承认了该规范可能不完善的可能性。这以一种重要的方式将该原则置于语境中。¶

不完美的规范是很自然的,主要是因为需要实现和部署比完善规范更重要。与任何复杂系统一样,协议也从其使用经验中受益匪浅。部署的协议比完美的协议有用得多。稳健性原则是一种适用于系统设计早期阶段的工具。¶

正如[成功]所表明的,协议的成功或失败更多地取决于有用性等因素,而不是技术上的卓越性。协议规范的及时发布,即使可能存在缺陷,也可能对互联网的最终成功做出重大贡献。¶

因此,问题不在于前提,而在于结论:公正原则本身。¶

将稳健性原则应用于早期互联网或任何处于部署早期阶段的系统是有利的。应用这些原则可以促进处理互操作性问题的努力,这将优先发展。然而,延迟可能会放大处理互操作性问题的最终成本。¶

随着时间的推移,规范的不同实现会出现。当语义组件的解释或表达发生变化时,实现就不再具有完美的互操作性。¶

实现缺陷通常被认为是导致变化的原因,尽管它是多种因素的组合。将协议应用于原始设计中未提及的用途,或规范中的歧义和错误通常是混淆因素。在阿普罗托考的使用期限内,对说明书的解释可能会出现分歧。¶

即使出于最好的意图,互操作的压力也可能很大。任何实现都无法避免无限制地用正确性换取互操作性。¶

以稳健原则中建议的方式对变化做出反应的实施会建立一个反馈周期。随着时间的推移:

实现逐步添加逻辑,以限制数据的传输方式,或允许接收内容发生变化。¶

缺陷可以成为事实标准。协议的任何实现都需要复制异常行为,否则无法互操作。这既是应用稳健性原则的结果,也是避免致命错误条件的自然不情愿的产物。确保这种环境中的互操作性通常被称为目标是";bugfor bug兼容";。¶

例如,在TLS[TLS]中,扩展使用标记长度值格式,可以按任何顺序将它们添加到消息中。但是,如果某些服务器实现遇到以空扩展名结尾的TLS ClientHellomessage,则会终止连接。为了保持互操作性,客户端实现需要注意这个错误,并确保aClientHello消息以非空扩展结尾。¶

最初的JSON规范[JSON]演示了规范缺陷的影响。RFC 4627省略了一系列关键细节,包括Unicode处理、对象成员的排序和复制,以及数字编码。因此,实现使用了一系列解释。更新后的规范[JSON-BIS]没有纠正这些错误,而是专注于识别JSON的可互操作子集。I-JSON[I-JSON]获取该子集,并定义一种新格式,禁止JSON中有问题的部分。当然,这意味着I-jsoni不能与JSON完全互操作。因此,I-JSON并没有在解析器中广泛实现。许多JSON解析器现在实现了[ECMA262]中指定的更精确的算法。¶

因此,稳健性原则鼓励人们做出可能导致互操作性问题的反应。尤其是,应用健壮性原则对新协议的早期实现尤其有害,因为早期实现中的怪癖可能会影响所有后续部署。¶

一旦偏差根深蒂固,纠正这种情况可能极其困难——如果不是不可能的话。¶

协议实施的互操作性要求由其他部署设置。规范和一致性测试套件(如果有)可以指导实现的初始开发,但实现结果需要与已部署的实现进行互操作。¶

对于广泛使用的协议,互联网的巨大规模使得大规模的可互操作性测试对于除少数特权之外的所有人都不可行。随着实现数量和bug的增加,使用逆向工程构建新实现的成本也在增加。更糟糕的是,广泛的互操作性所需的一系列调整可能很难发现。¶

因此,新的实现可能会被迫进入小众应用领域,从而可以更紧密地管理互操作性问题带来的问题。然而,将新的实现限制在有限的部署中可能会导致协议中出现分叉。如果实现不能互操作,就很难防止这些实现随着时间的推移出现更多分歧。¶

这对议定书的生态系统有负面影响。新的实施对于确保协议的持续可行性非常重要。新的协议实现也更有可能针对新的和多样化的用例进行开发,并且通常是现有用户受益的特性和功能的来源。¶

解决互操作性问题的需要也降低了已建立的实现进行更改的能力。针对互操作性问题的缓解措施的累积使得实现更难维护,并且无法训练可扩展性(另请参见[USE-IT])。¶

有时,看似互操作性的问题是协议设计问题的症候。一个愿意通过修改或扩展协议来对协议进行修改的社区,可以在这个过程中使协议变得更好。相反,应用稳健性原则会隐藏问题,使得以后更难甚至不可能解决问题。¶

健壮性原则可以非常有效地防止对等方执行协议时出现缺陷。尤其是当一个规范在很长一段时间内保持不变时,容忍错误的动机会随着时间的推移而累积。事实上,当面对一个不变规范的不同解释时,实现保持可互操作性的唯一方法是容忍解释和实现错误的差异。¶

从这个角度来看,将健壮性原则应用于实现不变的协议规范是合乎逻辑的,甚至是必要的。但这一结论依赖于一个假设,即现有规范和实现无法更改。以这种方式应用鲁棒性原则,会不成比例地重视短期收益,而不是对未来实现和整个协议的负面影响。¶

为了使协议具有持续的生存能力,除了处理随着时间的推移可能出现的新问题和旧问题外,规范和实现都必须对变化做出响应。¶

维护规范,使其与部署紧密匹配,可以确保实现一致的互操作性,并为新实现消除不必要的障碍。维护还可以持续改进协议。新的用例是协议可能成功的一个指标。¶

强烈鼓励协议设计人员在最初的开始和定义之后继续维护和改进协议规范。这可能需要制定修订后的规范、扩展或其他支持材料,以记录协议的当前状态。实施和部署协议的人员的参与是这一过程的关键部分,因为他们提供了关于如何使用协议的经验的输入。¶

大多数互操作性问题不需要修改协议或协议规范。例如,在对等网络中处理有效实现的最有效方法可能是向负责的开发人员发送电子邮件。从长远来看,修复一个孤立的bug要比处理变通方法的后果有效得多。¶

协议的早期实现有更严格的义务严格遵守规范,因为它们的行为将影响所有后续实现。除了规范之外,以后的实现将以现有部署所接受的内容为指导。在早期部署中容忍错误最有可能导致问题。在早期部署期间,协议规范可能需要更频繁的修订,以获取早期部署的反馈。¶

忽视会很快产生本文件所述的负面后果。将协议恢复到可以维护的状态需要首先在部署时发现协议的属性,而不是最初记录的协议。这可能既困难又耗时,尤其是当协议有多种实现时。在经过一段时间的最少维护后,HTTP[HTTP]就开始了这样一个过程。将HTTP规范恢复到relevancetook需要付出巨大的努力。¶

如果维护是响应性的,那么它是最有效的,这在很大程度上受部署协议更改的速度的影响。对于在较长时间范围内运行的协议部署,可能需要遵循鲁棒性原则的精神采取临时解决办法。如果规范比部署更容易更新,则可以记录解决方案的详细信息,包括不再需要解决方案时所需的协议形式,以及删除解决方案的计划。¶

良好的可扩展性[EXT]可以更容易地响应协议部署环境中的新用例或更改。¶

扩展协议的能力有时被误认为是应用了可靠性原则。毕竟,如果一方希望在另一方准备接收新功能之前就开始使用它,那么可能会假设接收方能够容忍意外的输入。¶

设计良好的可扩展性机制为处理新消息或参数等事项建立了明确的规则。这取决于对格式错误或非法输入的处理有明确的规则,以便在所有可能影响互操作的情况下实现行为一致。如果扩展机制和错误处理被正确地设计和实现,那么新的协议特性就可以在理解它们对现有实现的影响的情况下进行部署。¶

相比之下,依赖实现来一致地应用健壮性原则并不是一个好的可扩展性策略。如[EXT]附录a.3中的案例研究所示,使用协议的未记录或附带特性作为扩展机制的基础可能非常困难。¶

协议可以设计为允许一组有限的有效输入,也可以允许广泛的输入作为核心功能(例如参见[HTML])。指定和实施更灵活的协议更加困难;在没有充分理由保持灵活性的情况下,无许可可变性更可取。¶

明确规定的协议包括一致处理异常情况的规则。这增加了实现对异常情况进行一致且可互操作处理的可能性。¶

不容忍任何偏离规范的行为,在这种情况下,实现会因观察到未定义或异常的行为而产生默认错误,从而减少异常实现的发生。选择为未指定的条件生成致命错误,而不是尝试错误恢复,可以确保故障得到关注。¶

这尤其改善了对新实现的反馈。当一个新的实现遇到一个不能容忍错误的对等方时,它会收到强烈的反馈,从而能够快速发现问题。¶

为了有效,不容忍的实现需要得到足够广泛的部署,以使新的实现很可能遇到它们。这可能取决于部署严格检查的多个实现。¶

这并不意味着不容忍协议早期部署中的错误会影响互操作性。相反,当现有的实现遵循明确指定的错误处理时,新的实现或功能可以更容易地引入,因为对现有实现的影响可以很容易地预测;另见第6节。¶

任何不容忍也需要得到规范的有力支持,否则它们会导致协议社区的分裂或工作环境的扩散;见第8节。¶

不容忍可以用来激励遵守任何协议要求。例如,TTP/2[HTTP2]中的不充分的_安全错误代码和相关要求导致部署基地的安全性得到改善。¶

任何受维护引起的更改影响的协议参与者,如果他们不愿意或无法实施或部署对协议所做的更改,则可以将其排除在外。¶

故意排除有问题的实现是一个重要工具,可以确保协议的互操作性仍然可行。尽管可兼容的更改总是比不兼容的更改更可取,但并不总是能够产生一种保护所有当前和未来协议参与者互操作能力的设计。开发和部署具有排除以前互操作实现风险的更改需要一定的谨慎,但不应仅以排除风险为由阻止对协议的更改。¶

当选择不容忍错误时,排除是一个直接目标(见第7节)。排他性行为是为了保护未来的互操作性而故意采取的。¶

排除实施或部署可能会导致协议系统崩溃,这可能比遵循健壮性原则导致的任何分歧更有害。RFC 5704[UNCOORDINATED]描述了协议维护中的冲突或竞争如何导致类似问题。¶

草率的实现、对规范的松散解释,以及为弥补规范中的漏洞而对需求进行的不协调,都可能导致不安全问题。隐藏协议变化的后果会鼓励隐藏问题,这会隐藏错误并使其难以发现。¶

本文档中描述的问题的后果对于安全性依赖于协议元素语义一致性的任何协议来说都是特别严重的。对于ins

......