互联网工程任务组 (IETF) M. Thomson
请求注释: 8188 Mozilla
类别:标准轨道 2017 年 6 月
ISSN: 2070-1721

用于 HTTP 的加密内容编码


摘要

本备忘录引入了一种用于 HTTP 的内容编码,允许对消息有效载荷进行加密。

本备忘录的状态

这是一个互联网标准轨道文档。

本文档是互联网工程任务组 (IETF) 的产物。它代表了 IETF 社区的共识。它已接受公开审查并已获互联网工程指导小组 (IESG) 批准发布。有关互联网标准的更多信息,请参阅 RFC 7841 第 2 节

有关本文档的当前状态、任何勘误以及如何提供反馈的信息,可在 http://www.rfc-editor.org/info/rfc8188 获取。

Copyright Notice

Copyright (c) 2017 IETF Trust and the persons identified as the document authors. All rights reserved.

This document is subject to BCP 78 and the IETF Trust's Legal Provisions Relating to IETF Documents (http://trustee.ietf.org/license-info) in effect on the date of publication of this document. Please review these documents carefully, as they describe your rights and restrictions with respect to this document. Code Components extracted from this document must include Simplified BSD License text as described in Section 4.e of the Trust Legal Provisions and are provided without warranty as described in the Simplified BSD License.

1. 引言

有时希望对 HTTP 消息(请求或响应)的内容进行加密,以便在有效载荷被存储(例如通过 HTTP PUT)时,只有持有相应密钥的人才能读取它。

例如,可能需要在服务器上存储一个文件而不向该服务器披露其内容。此外,同一文件也可以复制到其他服务器(以提高对服务器或网络故障的抗性)、由客户端下载(以便离线访问)等,而不泄露其内容。

这些用途不能仅通过传输层安全(TLS)来满足,因为 TLS 仅加密客户端与服务器之间的通道。

本文档规定了一种内容编码(参见 RFC7231 第 3.1.2 节),以便在 HTTP 中实现上述及其他用例。

该内容编码并不是对消息级别加密格式的直接改编——例如 [RFC4880][RFC5652][RFC7516][XMLENC] 所描述的那些格式。那些格式并不适合流处理,而流处理是 HTTP 所需的。此处描述的格式更接近于 [RFC5116] 中描述的底层结构。

在消息级别加密格式使用相同原语的情况下,该格式可被视为具有特定配置文件的一系列加密消息。例如,附录 A 说明了该格式如何与一系列具有固定头的 JSON Web Encryption 值相一致。

该机制通常只是使用内容加密的更大设计的一小部分。客户端和服务器如何获取和识别密钥取决于具体用例。特别地,密钥管理系统并未在本文中描述。

1.1. 需求语言

本文档中以全部大写出现的关键字 "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY" 和 "OPTIONAL" 应按 BCP 14 中对于 RFC2119 的定义来解释(见 [RFC2119][RFC8174])。

2. “aes128gcm” HTTP 内容编码

“aes128gcm” HTTP 内容编码表示有效载荷已使用在 [RFC5116] 第 5.1 节中标识为 AEAD_AES_128_GCM 的基于 Galois/Counter 模式(GCM)的高级加密标准(AES)进行加密。AEAD_AES_128_GCM 算法使用 128 位的内容加密密钥。

使用此内容编码需要知道一个密钥。如何获取该密钥不在本文档范围内定义。

“aes128gcm” 内容编码使用一组固定的加密原语。如需密码算法可替换性,可通过定义新的内容编码方案来实现。这保证了仅通过 HTTP 的 Accept-Encoding 头字段即可协商是否使用加密。

“aes128gcm” 内容编码使用固定的记录大小。最终编码由一个头(见 第 2.1 节)和零个或多个固定大小的加密记录组成;最后一条记录可以小于记录大小。

记录大小决定了每部分明文被加密时的长度。记录大小("rs")包含在内容编码头中(见 第 2.1 节)。

+-----------+             content
|   data    |             any length up to rs-17 octets
+-----------+
     |
     v
+-----------+-----+       add a delimiter octet (0x01 or 0x02)
|   data    | pad |       then 0x00-valued octets to rs-16
+-----------+-----+       (or less on the last record)
         |
         v
+--------------------+    encrypt with AEAD_AES_128_GCM;
|    ciphertext      |    final size is rs;
+--------------------+    the last record can be smaller

AEAD_AES_128_GCM 产生的密文比其输入明文长 16 个八位字节。因此,每条记录未加密的内容比记录大小少 16 个八位字节。有效记录始终至少包含一个填充分隔字节和一个 16 字节的认证标签。

每条记录包含一个单一的填充分隔字节,后面跟任意数量的 0x00 字节。最后一条记录的填充分隔字节取值为 2,其他记录的填充分隔字节值为 1。

解密时,填充分隔字节是记录中最后一个非零字节。若记录不包含任何非零字节,则解密者 必须 失败。若最后一条记录的填充分隔字节值不是 2,或非最后一条记录的填充分隔字节值不是 1,则解密者 必须 失败。

每条记录的 nonce 是一个 96 位值,由记录序列号和输入密钥材料构成。随机数派生在 第 2.3 节 中说明。

传递给每次 AEAD_AES_128_GCM 调用的附加数据是一个长度为零的八位字节序列。

该记录结构的一个结果是,按记录大小粒度的范围请求(Range requests)和对加密有效载荷体的随机访问是可行的。范围的端点处的部分记录不能被解密。因此,最好让范围请求从记录边界开始并在记录边界结束。然而,请注意填充的存在可能会干扰对加密数据中特定部分的随机访问。

为特定情形选择最合适的记录大小需要权衡。较小的记录大小允许更快地释放解密后的八位字节,这对依赖响应性的应用是有利的。较小的记录也减少了在需要对密文进行随机访问时所需的额外数据。

不依赖流处理、随机访问或任意填充的应用可以使用较大的记录,甚至单条记录。较大的记录可以减少处理和数据开销。

2.2. 内容加密密钥派生

为了允许为多个不同的 HTTP 消息重用密钥材料,为每条消息派生一个内容加密密钥。内容加密密钥通过将 “salt” 参数与 HMAC 基的密钥派生函数(HKDF,见 [RFC5869])结合使用,并采用 SHA-256 哈希算法(见 [FIPS180-4])来派生。

“salt” 参数的值作为 HKDF 的 salt 输入。由 “keyid” 参数标识的密钥材料作为 HKDF 的输入密钥材料(IKM)。输入密钥材料预计由接收方另行提供。HKDF 的 extract 阶段将产生伪随机密钥(PRK),如下所示:

   PRK = HMAC-SHA-256 (salt, IKM)

HKDF 的 info 参数被设置为 ASCII 编码字符串 "Content-Encoding: aes128gcm" 并追加单个零八位字节:

   cek_info = "Content-Encoding: aes128gcm" || 0x00
注(1):
八位字节序列的连接由 || 运算符表示。
注(2):
此处及第 2.3 节 中使用的字符串不包含在某些编程语言中常见的终止 0x00 八位字节。

AEAD_AES_128_GCM 需要一个 16 八位字节(128 位)的内容加密密钥(CEK),因此 HKDF 的长度参数 L 为 16。HKDF 的第二步可以简化为对单个 HMAC 的前 16 个八位字节:

   CEK = HMAC-SHA-256(PRK, cek_info || 0x01)

2.3. 随机数(Nonce) 派生

输入到 AEAD_AES_128_GCM 的 nonce 为每条记录构造。每条记录的 nonce 为 12 八位字节(96 位)值,由记录序列号、输入密钥材料和 “salt” 参数派生而成。

输入密钥材料和 “salt” 参数作为不同的 info 和长度(L)参数输入到 HKDF 中。

长度参数 L 为 12 八位字节。用于 nonce 的 info 参数为 ASCII 编码字符串 "Content-Encoding: nonce",并以单个 0x00 八位字节终止:

   nonce_info = "Content-Encoding: nonce" || 0x00

所得结果与记录序列号通过按位异或组合以产生 nonce。记录序列号(SEQ)为一个以网络字节序表示的 96 位无符号整数,从零开始。

因此,每条记录的最终 nonce 为 12 八位字节值:

   NONCE = HMAC-SHA-256(PRK, nonce_info || 0x01) XOR SEQ

该 nonce 构造防止记录的删除或重排序。

3. 示例

本节展示了加密内容编码的一些示例。

注:本节示例中的所有二进制值使用带 URL 与文件名安全字母表的 base64 编码(见 [RFC4648])。这包括请求体。为适应排版限制,示例中加入了空白和换行。

3.1. 响应的加密

此处展示了对一次成功的 HTTP GET 响应进行加密的情形。该示例使用 4096 八位字节的记录大小且无填充(仅有单字节填充分隔符),因此只有一个部分记录存在。输入密钥材料由空字符串标识(即头中 "keyid" 字段长度为零)。

本示例中的加密数据为 UTF-8 编码字符串 "I am the walrus"。输入密钥材料的值为 "yqdlZ-tYemfogSmv7Ws5PQ"(以 base64url 表示)。54 八位字节的内容体只包含单条记录,这里为展示目的用 71 个 base64url 字符呈现。

HTTP/1.1 200 OK
Content-Type: application/octet-stream
Content-Length: 54
Content-Encoding: aes128gcm

I1BsxtFttlv3u_Oo94xnmwAAEAAA-NAVub2qFgBEuQKRapoZu-IxkIva3MEB1PD-
ly8Thjg

注意,为避免暴露有关内容的信息,已将媒体类型更改为 "application/octet-stream"。或者(等效地),也可以省略 Content-Type 头字段。

此示例的中间值(均以 base64url 显示):

salt (from header) = I1BsxtFttlv3u_Oo94xnmw
PRK = zyeH5phsIsgUyd4oiSEIy35x-gIi4aM7y0hCF8mwn9g
CEK = _wniytB-ofscZDh4tbSjHw
NONCE = Bcs8gkIRKLI8GeI8
unencrypted data = SSBhbSB0aGUgd2FscnVzAg

3.2. 多记录加密

本示例显示相同消息但输入密钥材料为 "BO3ZVPxUlnLORbVGMpbT1Q"。在该示例中,明文被分割为每条 25 八位字节的记录(即头部中 "rs" 字段为 25)。第一条记录包含一个 0x00 填充字节。这意味着第一条记录中有 7 个消息字节,第二条记录中有 8 个。头中还包含 UTF-8 编码字符串 "a1" 的密钥标识符。

HTTP/1.1 200 OK
Content-Length: 73
Content-Encoding: aes128gcm

uNCkWiNYzKTnBN9ji3-qWAAAABkCYTHOG8chz_gnvgOqdGYovxyjuqRyJFjEDyoF
1Fvkj6hQPdPHI51OEUKEpgz3SsLWIqS_uA

4. 安全性考虑

该机制假定存在一个密钥管理框架,用于在合法发送方和接收方之间管理密钥分发。定义密钥管理是将该机制整合到更大应用、协议或框架中的一部分。

加密(尤其是密钥管理)的实现可能很困难。例如,实现需要考虑通过侧信道泄露密钥材料的可能性,例如某一操作所需时间可能暴露信息。对加密算法的良好实现要求可能会随时间变化。

4.1. 自动解密

作为一种内容编码,“aes128gcm” 可能会被接收方自动移除,而这一行为对消息的最终使用者并不明显。依赖于使用该机制进行内容来源认证的接收方必须 拒绝未包含 “aes128gcm” 内容编码的消息。

4.2. 消息截断

该内容编码被设计为允许对大消息进行增量处理,并在有限范围内允许对明文进行随机访问。该内容编码允许接收方检测消息是否被截断。

部分传送的消息 不得 被当作完整消息来处理。例如,不得将部分传送的消息缓存为完整消息。

攻击者可能利用处理部分消息的意愿来使接收方保持在特定的中间状态。对部分消息进行处理的实现需要确保任何中间处理状态不会为攻击者提供优势。

4.3. 密钥与随机数重用

在 AES-GCM 中,用相同的内容加密密钥和 nonce 加密不同的明文是不安全的(见 [RFC5116])。本方案使用固定的 nonce 递增序列。因此,每次应用内容编码都需要一个新的内容加密密钥。由于输入密钥材料可以重用,因此需要唯一的 “salt” 参数来确保不会重用内容加密密钥。

如果重用了内容加密密钥——即重用了输入密钥材料和 “salt” 参数——这可能会暴露明文和认证密钥,从而使加密保护失效。因此,若重用相同的输入密钥材料,则“salt” 参数每次 必须 唯一。实现者 应该 为每条消息生成随机的 “salt”。

4.4. 数据加密限制

AEAD_AES_128_GCM 可加密的数据存在限制。记录大小的最大值受头中 “rs” 字段的限制(见 第 2.1 节),这能确保单次 AEAD_AES_128_GCM 应用不会达到 2^36-31 的限制(见 [RFC5116])。为在选择明文攻击下保持 2^-40 的不可区分性概率,使用相同输入密钥材料和 salt 派生的密钥所能加密的明文总量 必须 少于 2^44.5 个 16 八位字节的块(见 [AEBounds])。

如果记录大小是 16 的倍数,这表示可安全加密的数据量为 398 TB(包括填充和开销)。但若记录大小不是 16 的倍数,因加密部分 AES 块,安全加密的数据总量会减少。最坏情况是记录大小为 18 八位字节,此时最多可加密 74 TB 明文,其中至少一半为填充。

4.5. 内容完整性

该机制仅提供内容来源认证。认证标签仅保证具有内容加密密钥的实体生成了加密数据。

因此,任何拥有内容加密密钥的实体都可以生成将被接受为有效的内容。这包括相同 HTTP 消息的所有接收方。

此外,任何能够修改 Content-Encoding 头字段和 HTTP 消息体的实体都可以替换内容。若无内容加密密钥或输入密钥材料,则无法修改或替换载荷体的部分内容。

4.6. 在头字段中泄露信息

由于仅对载荷体进行加密,头字段中暴露的信息对任何能读取 HTTP 消息的人都是可见的。这可能会泄露侧信道信息。

例如,Content-Type 头字段可能会泄露有关载荷体的信息。

根据应用的威胁模型和用户对信息泄露的容忍度,可以采用多种策略来缓解该威胁:

  1. 判断这不是问题。例如,如果预计所有存储的内容都是 "application/json" 或其他非常常见的媒体类型,则暴露 Content-Type 头字段可能是可接受的风险。
  2. 如果被认为是敏感信息且可以通过其他方式确定(例如带外、在其他表示中给出提示等),则省略相关头并/或对其进行规范化。在 Content-Type 的情况下,可以始终发送 Content-Type: application/octet-stream(最通用的媒体类型),或根本不发送 Content-Type。
  3. 如果被认为是敏感信息且无法通过其他方式传达,则使用 application/http 媒体类型封装 HTTP 消息(参见 RFC7230 第 8.3.2 节),并将其作为“外层”消息的载荷进行加密。

4.7. 污染存储

该机制仅提供数据来源认证;它并不对消息创建者做认证或授权,这些仍可能需要另行执行(例如通过 HTTP 认证 [RFC7235])。

当服务器在不解密载荷的情况下接受 HTTP PUT 请求时,这尤其重要;如果请求未经过认证,第三方就可能拒绝服务和/或污染存储。

4.8. 大小与时序攻击

使用该机制的应用需要注意,加密消息的大小及其时序、HTTP 方法、URI 等可能会泄露敏感信息。参见例如 [NETFLIX][CLINIC]

该风险可以通过本机制提供的填充来缓解。或者,将内容拆分成段并分别存储也可能减少暴露。HTTP/2 结合 TLS 也可用于隐藏单个消息的大小。

制定填充策略很困难。良好的填充策略依赖上下文。常见策略包括填充到一组固定长度、填充到某个值的倍数或填充到 2 的幂。即便是良好的策略也可能在接收方处理活动可被观察时导致大小信息泄露。若消息的尾部记录仅包含填充,则尤为如此。建议将非填充数据分散到各记录中以避免泄露大小信息。

5. IANA 注意事项

5.1. “aes128gcm” HTTP 内容编码

本备忘录在 “HTTP Content Coding Registry” 中注册 “aes128gcm” HTTP 内容编码,如 第 2 节 所述。

  • Name: aes128gcm
  • Description: 使用 128 位内容加密密钥的 AES-GCM 加密
  • Reference: 本规范

6. 参考文献

6.1. 规范性参考文献

[FIPS180-4]
National Institute of Standards and Technology, “Secure Hash Standard (SHS)”, FIPS PUB 180-4, DOI 10.6028/NIST.FIPS180-4, August 2015, <http://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.180-4.pdf>.
[RFC2119]
Bradner, S., “Key words for use in RFCs to Indicate Requirement Levels”, BCP 14, RFC 2119, DOI 10.17487/RFC2119, March 1997, <https://www.rfc-editor.org/info/rfc2119>.
[RFC3629]
Yergeau, F., “UTF-8, a transformation format of ISO 10646”, STD 63, RFC 3629, DOI 10.17487/RFC3629, November 2003, <https://www.rfc-editor.org/info/rfc3629>.
[RFC5116]
McGrew, D., “An Interface and Algorithms for Authenticated Encryption”, RFC 5116, DOI 10.17487/RFC5116, January 2008, <https://www.rfc-editor.org/info/rfc5116>.
[RFC5869]
Krawczyk, H. and P. Eronen, “HMAC-based Extract-and-Expand Key Derivation Function (HKDF)”, RFC 5869, DOI 10.17487/RFC5869, May 2010, <https://www.rfc-editor.org/info/rfc5869>.
[RFC7230]
Fielding, R., Ed. and J. Reschke, Ed., “Hypertext Transfer Protocol (HTTP/1.1): Message Syntax and Routing”, RFC 7230, DOI 10.17487/RFC7230, June 2014, <https://www.rfc-editor.org/info/rfc7230>.
[RFC7231]
Fielding, R., Ed. and J. Reschke, Ed., “Hypertext Transfer Protocol (HTTP/1.1): Semantics and Content”, RFC 7231, DOI 10.17487/RFC7231, June 2014, <https://www.rfc-editor.org/info/rfc7231>.
[RFC8174]
Leiba, B., “Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words”, BCP 14, RFC 8174, DOI 10.17487/RFC8174, May 2017, <https://www.rfc-editor.org/info/rfc8174>.

附录 A. JWE 映射

“aes128gcm” 内容编码可被视为一系列 JSON Web Encryption (JWE) 对象,每个对象对应一条包含尾部填充的固定大小记录。对可能用 JWE Compact 序列化表达的 JWE 对象进行如下变换:

  • JWE Protected Header 固定为 { "alg": "dir", "enc": "A128GCM" },描述使用 AES-GCM 的直接加密(128 位 CEK)。该头不传输,而是由 Content-Encoding 头字段的值暗示。
  • JWE Encrypted Key 为空,如直接加密算法所规定。
  • 每条记录的 JWE Initialization Vector ("iv") 被设置为记录序列号(从零开始)的 96 位值与从输入密钥材料派生的值的按位异或。该值也不传输。
  • 最终值为连接的头、JWE 密文和 JWE 认证标签,均不进行 base64url 编码。省略 “.” 分隔符,因为这些字段的长度是已知的。

因此,第 3.1 节 中的示例可以用 JWE Compact 序列化渲染为:

eyAiYWxnIjogImRpciIsICJlbmMiOiAiQTEyOEdDTSIgfQ..Bcs8gkIRKLI8GeI8.
-NAVub2qFgBEuQKRapoZuw.4jGQi9rcwQHU8P6XLxOGOA

其中第一行表示固定的 JWE Protected Header、空的 JWE Encrypted Key 和算法确定的 JWE Initialization Vector。第二行包含被编码的主体,分为 JWE Ciphertext 和 JWE Authentication Tag。

致谢

Mark Nottingham 是本文档的原始作者之一。

以下人员提供了有价值的意见:Richard Barnes、David Benjamin、Peter Beverloo、JR Conlin、Mike Jones、Stephen Farrell、Adam Langley、James Manger、John Mattsson、Julian Reschke、Eric Rescorla、Jim Schaad 和 Magnus Westerlund。

作者地址

Martin Thomson
Mozilla
EMail: martin.thomson@gmail.com