跳到主要内容

7. 安全考虑 (Security Considerations)

本节描述 HPACK 中可能引发安全关注的领域:

  • 将 compression 用作基于长度的 oracle, 以验证对 secret 的猜测, 而这些 secret 被 compression 到共享的 compression context 中.

  • 解码器的处理能力或内存容量被耗尽而导致拒绝服务.

7.1. 探测动态表状态 (Probing Dynamic Table State)

HPACK 通过利用 HTTP 等协议固有的冗余, 减少 header field 编码的长度. 这样做的最终目标是减少发送 HTTP 请求或响应所需的数据量.

用于编码 header field 的 compression context 可以被攻击者探测, 前提是该攻击者既能定义要编码和传输的 header field, 又能观察这些字段编码后的长度. 当攻击者同时具备这两种能力时, 就可以自适应地修改请求, 以确认对 dynamic table 状态的猜测. 如果某个猜测被 compression 成更短的长度, 攻击者就可以观察编码后的长度, 并推断该猜测是正确的.

即使使用传输层安全 (Transport Layer Security, TLS) 协议也可能发生这种情况 (见 [TLS12]), 因为 TLS 虽然为内容提供机密性保护, 但对内容长度只提供有限保护.

Note: 填充方案只能针对具备这些能力的攻击者提供有限保护, 可能只是迫使攻击者增加猜测次数, 才能获知与某个给定猜测相关联的长度. 填充方案还会通过增加传输位数, 直接抵消 compression 的效果.

CRIME [CRIME] 等攻击证明了这类通用攻击者能力的存在. 该具体攻击利用了 DEFLATE [DEFLATE] 基于前缀匹配移除冗余这一事实. 这使攻击者能够逐字符确认猜测, 将指数时间攻击降低为线性时间攻击.

7.1.1. 对 HPACK 和 HTTP 的适用性 (Applicability to HPACK and HTTP)

HPACK 通过强制猜测必须匹配整个 header field 值而不是单个字符, 缓解了以 CRIME [CRIME] 为模型的攻击, 但并不能完全阻止这类攻击. 攻击者只能得知某个猜测是否正确, 因此只能对 header field 值进行暴力猜测.

因此, 恢复特定 header field 值是否可行取决于这些值的熵. 因而, 高熵值不太可能被成功恢复. 但是, 低熵值仍然易受攻击.

只要两个互不信任的实体控制的请求或响应被放到同一个 HTTP/2 连接上, 这种性质的攻击就可能发生. 如果共享的 HPACK compressor 允许一个实体向 dynamic table 添加条目, 并允许另一个实体访问这些条目, 则表状态可能被获知.

当中介满足以下任一情况时, 会出现来自互不信任实体的请求或响应:

  • 在通向 origin server 的单个连接上发送来自多个客户端的请求, 或

  • 从多个 origin server 获取响应, 并将这些响应放到通向客户端的共享连接上.

Web 浏览器还需要假定, 由不同 web origin (见 [ORIGIN]) 在同一连接上发出的请求来自互不信任的实体.

7.1.2. 缓解措施 (Mitigation)

HPACK 的使用者可以通过限制互不信任实体向 dynamic table 添加条目以及观察 dynamic table 状态的能力, 来降低 dynamic table 探测的可能性.

最有效的缓解措施是避免在并非同一 origin 行为所产生的请求或响应之间共享连接或 compression context.

对于中介, 这意味着来自不同客户端的请求和来自不同 origin server 的响应 MUST NOT 共享单个 HPACK compression context.

对于 user agent, 这意味着针对不同 origin 的请求 MUST 分离 compression context, 除非已知这些 origin 受共同控制.

7.1.3. 永不索引字面量 (Never-Indexed Literals)

实现也可以选择不对敏感 header field 进行 compression, 而是将其值编码为 literal, 从而保护这些字段.

拒绝为 header field 生成 indexed representation 只有在所有 hop 都避免 compression 时才有效. never-indexed literal 位 (见 Section 6.2.3) 可用于向中介发出信号, 表明某个特定值是有意作为 literal 发送的.

中介 MUST NOT 使用会对其建立索引的另一种 representation 重新编码采用 never-indexed literal representation 的 header field. 如果使用 HPACK 进行重新编码, 可以改用不带索引的 literal representation (见 Section 6.2.2).

是否对某个 header field 使用 never-indexed literal representation 取决于若干因素. 由于 HPACK 不能防止对整个 header field 值的猜测, 较短或低熵的值更容易被对手恢复. 因此, encoder 可能选择不索引低熵值.

encoder 也可能选择不索引那些被认为恢复价值很高或非常敏感的 header field 值, 例如 Cookie 或 Authorization header field.

7.2. 静态 Huffman 编码 (Static Huffman Encoding)

目前尚无已知的针对 static Huffman encoding 的攻击. 一项研究表明, 使用 static Huffman encoding 表会造成信息泄露; 但是, 同一研究也得出结论, 攻击者无法利用这种信息泄露恢复任何有意义数量的信息 (见 [PETAL]).

7.3. 内存消耗 (Memory Consumption)

攻击者可以尝试使端点耗尽内存. HPACK 被设计为限制端点分配内存的峰值和稳定数量.

compression context 使用的内存量可以通过设置 dynamic table 的最大大小来限制. 在 HTTP/2 中, 这由 SETTINGS_HEADER_TABLE_SIZE 设置控制 (见 [HTTP2] 的 Section 6.5.2).

encoder 可以通过控制 dynamic table 大小, 限制 decoder 处可创建的状态量. 在 HTTP/2 中, decoder 通过使用 SETTINGS_HEADER_TABLE_SIZE 设置报告这一点. encoder MUST 将 dynamic table 大小限制为 decoder 报告的值, 从而控制已提交的内存量.

decoder 可以通过发出低于默认值的 SETTINGS_HEADER_TABLE_SIZE 值, 限制提交给 dynamic table 的内存量. 在 HTTP/2 中, 默认值为 4,096 octets, 这意味着除非 decoder 通过在 SETTINGS frame 中发送 SETTINGS_HEADER_TABLE_SIZE 设置来更改该值, 否则 encoder 最多可以为 dynamic table 使用这一数量的内存.

7.4. 实现限制 (Implementation Limits)

实现必须为其接受的整数值和 string literal 大小设置限制. 同样, 它还必须为将存储在 dynamic table 中的条目数量设置限制. 不设置限制可能会使实现暴露于攻击之下, 类似于在未设置合理限制的协议实现中已经观察到的情况.