EFAIL:过时的加密标准是罪魁祸首
我对最近发布的efail漏洞有很多想法,因此决定开始写一些相关内容。我暂时跳过关于披露过程的公众愤怒,主要想探讨技术问题,解释我认为哪里出了问题,以及未来如何变得更安全。我读到很多错误的说法,称“问题只出在邮件客户端”,而底层加密标准是好的,因此我将首先解释为什么我认为OpenPGP和S/MIME标准是坏的,以及为什么我们在2018年仍然看到这类漏洞。我计划写第二篇文章,标题为“efail:HTML邮件是罪魁祸首”。
我假设大多数人已经听说过efail,但快速版本是这样的:通过结合加密模式的弱点和HTML电子邮件,一组研究人员能够想出多种方式,诱骗邮件客户端泄露加密电子邮件的内容。并非所有攻击场景都涉及加密,但那些涉及加密的场景利用了加密模式的一种称为可塑性的属性。这意味着在某些情况下,你可以对加密消息的内容进行受控更改。
加密的可塑性并不是新事物。早在九十年代,人们就发现这可能是一个问题,并开始为加密添加认证。因此,你不仅保证加密数据不能被攻击者解密,还保证攻击者无法在没有密钥的情况下更改数据。在早期协议中,人们以临时方式实现认证,导致不同方法的安全性各不相同(通常称为MAC-then-Encrypt、Encrypt-then-MAC、Encrypt-and-MAC)。OpenPGP标准还添加了一种称为MDC(修改检测代码)的认证手段,而S/MIME标准从未获得类似的东西。
认证加密
2000年,Bellare和Namprempre引入了认证加密的概念。它可以概括为这样一个想法:与其在加密之上添加认证,不如定义一些构造,其中两者的组合以安全的方式标准化。它还改变了密码的定义,这后来变得相关,因为这篇早期论文已经为如何设计适当的认证加密API提供了良好指导。虽然未认证的密码具有接受输入并产生输出的解密功能,但认证密码的解密功能要么产生输出,要么产生错误(但不能两者兼有):
在这种方案中,发送方应用的加密过程接受密钥和明文以返回密文,而接收方应用的解密过程接受相同的密钥和密文以返回明文或一个特殊符号,指示其认为密文无效或不真实。(Bellare, Namprempre, Asiacrypt 2000 Proceedings)
该概念后来扩展为具有附加数据的认证加密(AEAD)的想法,意味着你可以有未加密但仍经过认证的部分。这在某些情况下很有用,例如,如果你将消息拆分为多个部分,则可以认证排序。今天,我们有许多标准化的AEAD模式。
始终使用认证加密
认证加密是一个很有意义的概念。设计加密系统时最基本的建议之一应该是:“除非你有非常好的理由不这样做,否则始终使用标准化的、现成的认证加密模式。”
如果人们使用了AEAD模式,许多攻击本可以避免。SSL/TLS中的填充Oracle攻击,如Vaudenay攻击及其变种如Lucky Thirteen攻击?使用AEAD就安全了。SSH中的部分明文发现,如2009年发现——2016年再次发现,因为修复无效?使用AEAD就安全了。由于字符编码错误而破坏的XML加密?如果你使用了AEAD,这本来可以避免。听说过2016年发现的iMessage漏洞吗?缺乏AEAD是原因。Owncloud加密模块被破坏?如果他们使用了AEAD。(我包括这一点是因为这是我自己对该主题的微小贡献。)
鉴于这一长串攻击,你会期望每个人得到的最基本建议之一是:“只要可能,始终使用AEAD。”这应该是加密101,但不知何故,事实并非如此。
教授90年代的最佳加密
不久前,在我订阅的一个密码学邮件列表中,有人发布了一个大学加密入门讲座材料的链接,称这是对该主题的良好介绍。我简要看了一下,回答说我认为它并不特别好,并列举了多个问题,其中之一是该讲座涵盖的密码模式是ECB、CBC、OFC、CFB和CTR。这些模式都没有经过认证。它们都不应该在任何现代加密系统中使用。
几周后,我在一个会议上,发现对面桌的人是一位密码学教授。我们开始讨论密码学教学,因为我发表了一些挑衅性言论(类似于“大学教的是过时的加密,然后我们最终得到坏的加密系统”)。所以我问他:“你在加密讲座中教哪些密码模式?”
答案:ECB、CBC、OFC、CFB和CTR。
之后,我搜索了加密入门讲座——令我惊讶的是,这 surprisingly 常见。这五种密码模式列表出于某种原因似乎是加密入门的流行选择。
这似乎没有太多意义。让我们快速浏览一下:ECB是对称分组密码最幼稚的加密方式,你用相同的密钥独立加密输入的每个块。我倾向于说它不是一个真正的加密模式,更像是一个反例。如果你见过著名的“ECB Tux”——那就是问题所在。
CBC(密码块链接)是一种广泛使用的模式,特别是它长期以来是TLS中最流行的模式,教授它以理解攻击是有意义的,但你不应该使用它。CFB模式并不广泛使用,我相信唯一广泛使用的实际上是在OpenPGP中。OFB更加晦涩,我不知道有任何主流协议使用它。CTR(计数器模式)在某种程度上是相关的,因为最流行的AEAD模式之一是计数器模式的扩展——它称为Galois/计数器模式(GCM)。
我认为公平地说,在加密入门讲座中教授这些密码列表是奇怪的。其中一些晦涩,一些 outright 危险,最重要的是:它们都不应该被使用,因为它们都没有经过认证。那么为什么这五种密码如此流行?是否有一个秘密列表,每个人在选择涵盖哪些密码时都使用?
实际上……是的,有这样一个列表。这些正是Bruce Schneier的书《应用密码学》中涵盖的五种密码模式——出版于1996年。
现在不要误解我:《应用密码学》无疑是密码学历史的重要组成部分。当它出版时,它可能是你能得到的最好的密码学入门资源之一。它涵盖了1996年可用的最佳加密。但自那以后我们学到了一些东西,其中之一是你最好使用认证加密模式。
还有更多:在今年的Real World Crypto会议上,提交了一篇论文,测试了加密API的可用性。该论文最初发表在IEEE安全与隐私研讨会上。我简要看了一下论文,这句话引起了我的注意:
“我们将ECB评分为不安全的操作模式,并将密码块链接(CBC)、计数器模式(CTR)和密码反馈(CFB)评分为安全。”
这些话写于2017年的一篇同行评审论文中。难怪我们在2018年仍然在与填充Oracle和密文可塑性作斗争。
选择认证模式
如果我们同意认证加密模式有意义,下一个问题是选择哪一个。这很容易为另一篇文章提供材料,但我会尽量简短。
最常见的模式是GCM,通常与AES密码结合使用。GCM有一些问题。正确实现它并不容易,并且会发生实现缺陷。搞乱nonce生成可能具有灾难性后果。你可以轻松收集一堆著名密码学家说GCM坏话的引用。
然而,尽管有所有批评,使用GCM仍然不是一个坏选择。如果你使用一个经过充分测试的标准实现并且不搞乱nonce生成,你就没问题。这句话来自一个参与发现我认为是唯一针对TLS中GCM的实际攻击的人。
其他流行模式是Poly1305(通常与Chacha20密码结合使用,但它也适用于AES)和OCB。OCB有一些不错的属性,但它有专利。虽然专利持有人允许某些用途,但这仍然造成了足够的不确定性,阻止了广泛部署。
如果你可以牺牲性能并且担心nonce生成问题,你可以看看AES在SIV模式中。此外,目前正在运行一个竞赛来选择未来的AEAD。
说了所有这些:选择任何标准化的AEAD模式都比根本不使用AEAD好。
两种电子邮件加密标准——OpenPGP和S/MIME——都非常古老。它们起源于90年代,并且只随时间进行了 minor 更新。
S/MIME是坏的,可能无法挽救
S/MIME默认使用CBC加密模式,没有任何认证。CBC具有可塑性,攻击者可以通过位翻转操纵加密内容,但这会破坏后续块。如果攻击者知道单个块的内容,那么他基本上可以构造任意密文,每第二个块是垃圾。
结合S/MIME密文部分易于预测的事实,这基本上意味着S/MIME游戏结束。攻击者可以构造任意邮件(填充一些垃圾块,但至少在HTML中它们可以轻松隐藏)并将原始邮件内容放在他喜欢的任何位置。这是efail攻击的核心思想,对于S/MIME,它直接有效。
有一个RFC来指定加密消息语法中的认证加密模式,这是S/MIME的基础格式,然而它没有被引用在最新的S/MIME标准中,因此不清楚如何使用它。
HTML邮件只是S/MIME最明显的问题。也可以构造具有 exfiltration 通道的恶意PDF或其他文档格式。即使没有这些,你也不希望在任何情况下有密文可塑性。S/MIME完全缺乏认证的事实意味着它在设计上是不安全的。
鉴于电子邮件加密最糟糕的事情之一总是有两个竞争的不兼容标准,这实际上可能是一个机会。讽刺的是,如果你一直在使用S/MIME并且想要类似的东西,你最好的选择可能实际上是切换到OpenPGP。
OpenPGP - CFB模式和MDC
对于OpenPGP,关于认证加密的情况有点复杂。OpenPGP引入了一种称为消息检测代码(MDC)的认证形式。MDC通过计算明文消息的SHA-1哈希,然后加密该哈希并将其附加到加密消息中来工作。
第一个问题是这是否是一个健全的密码构造。正如我上面所说,通常建议使用标准化的AEAD模式。很明显,CFB/MDC不是这样的东西,但这并不会自动使其不安全。虽然我不会建议在任何新协议中使用MDC,并且我认为用适当的AEAD模式替换它会很好,但它似乎没有任何明显的弱点。有些人可能指出使用SHA-1,由于构造碰撞的可能性,它被认为是一个坏的哈希函数。然而,在MDC的情况下,这似乎无法以任何方式被利用。
因此,在密码学上,虽然MDC看起来不是一个好的构造,但它似乎不是 immediate 的安全问题。然而,MDC在OpenPGP标准中的指定方式有两个主要问题,我认为公平地说OpenPGP也因此是坏的。
第一个问题是实现应该如何处理MDC标签无效或缺失的情况。这是规范必须说的:
任何MDC失败都表明消息已被修改,必须被视为安全问题。失败包括哈希值差异,但也包括缺少MDC包,或MDC包不在明文末尾的任何位置。任何失败都应该报告给用户。
这一点也不清楚。它必须被视为安全问题,但不清楚这意味着什么。失败应该报告给用户。阅读这一点,非常合理地认为,一个邮件客户端会显示一个带有缺失或坏MDC标签的邮件给用户,并附有警告,这完全符合规范。然而,这正是易受efail攻击的场景。
为了防止可塑性攻击,客户端必须防止在认证损坏时显示解密内容。这也回到了我上面引用的认证加密的定义。解密功能应该输出正确的明文或错误。
然而,这不是标准所说的,也不是GnuPG所做的。如果你解密一个MDC损坏的消息,你仍然会得到明文,然后才出错。
还有第二个问题:出于向后兼容的原因,MDC是可选的。OpenPGP标准有两种加密数据包类型:对称加密(SE)包(没有MDC)和对称加密完整性保护(SEIP)包(有MDC)。除了MDC,它们 mostly 相同,这意味着可以将有保护的包转换为无保护的包,这是一种在2015年发现的攻击。
这本来可以避免,例如通过对不同的包类型使用不同的密钥派生函数。但这没有发生。这意味着任何仍然支持旧SE包类型的实现都易受密文可塑性攻击。
对OpenPGP的好消息是,通过一些修改,它可以变得安全。如果一个实现丢弃MDC损坏或缺失的包,并选择不支持未认证的SE包,那么就没有 immediate 的密码漏洞。(仍然有HTML邮件和多部分消息的问题,但这独立于密码标准。)
流式和分块
如上所述,当用GnuPG解密一个MDC缺失或损坏的文件时,它会首先输出密文,然后出错。这违反了认证加密的定义,也可能是许多邮件客户端易受efail攻击的原因。这是一个邀请误用的API。然而,GnuPG这样行为有一个原因:大块数据的流式传输。
如果你想以从不输出未认证明文的方式设计GnuPG,你必须缓冲所有解密文本,直到你可以检查MDC。如果你加密大块数据,例如备份tarballs,这变得不可行。用AEAD模式替换CFB/MDC组合也不会自动解决这个问题。使用像GCM这样的模式,你仍然可以边解密数据边检查认证。
为了支持流式和适当的认证加密,一种可能性是将数据切割成具有最大大小的块。这或多或少是TLS所做的。
一个构造可能看起来像这样:输入数据以块处理——比如说——8千字节大小。确切大小是开销和流式速度之间的权衡,但几千字节范围内的东西肯定会工作。每个块将包含一个数字,该数字是认证附加数据的一部分,以防止重新排序攻击。最终块还将在附加数据中包含一个特殊指示符,因此可以检测截断。解密工具然后解密每个块并仅输出认证内容。(我不是自己想出这个的,正如所说,它接近TLS所做的,Adam Langley在一个你可以在这里找到的演讲中解释得很好。他甚至提到了导致efail的GnuPG特定问题。)
值得注意的是,这仍然可能以错误的方式实现。一个实现可以处理块的部分并在认证之前输出它们。在我第一次听说efail后不久,我想知道类似的事情是否会在TLS中发生。例如,浏览器可能在接收到半个TLS记录时就开始渲染内容。
即将到来的新OpenPGP标准
已经有一个未来版本OpenPGP标准的草案。它引入了两种认证加密模式——OCB和EAX——这是有些人想要OCB而其他人担心专利问题之间的妥协。我看不出有两种模式如何帮助 here,因为最终你只能实际使用一种模式,如果它得到广泛支持。
草案还支持消息分块。然而,现在它没有定义块大小的上限,你可以在单个块中有千兆字节的数据。支持那可能再次导致不安全的流式API。但引入块限制并要求API可能从不暴露未认证明文是一个 minor 更改。
不幸的是,草案的工作 mostly 停滞。虽然最新草案来自一月,但OpenPGP工作组去年因缺乏兴趣而关闭。
结论
正确使用认证加密模式可以防止许多问题。这在OpenPGP中是一个已知问题,但直到现在还不够紧迫来修复它。好消息是,通过 minor 修改,OpenPGP仍然可以安全使用。并且拥有具有适当认证加密的未来OpenPGP标准绝对是可能的。对于S/MIME,情况更加严峻,可能最好放弃它。首先,拥有竞争