| 互联网工程任务组 (IETF) | D. Schinazi |
| 请求评注: 9729 | Google LLC |
| 类别: 标准轨道 | D. Oliver |
| ISSN: 2070-1721 | Guardian Project |
| J. Hoyland | |
| Cloudflare Inc. | |
| 2025 年 2 月 |
隐蔽的 HTTP 认证方案
摘要
大多数 HTTP 认证方案是可探测的,即未认证的客户端可以探测某个源是否提供需要认证的资源。源可以通过不产生 Unauthorized 状态码来隐藏其需要认证的事实;然而,这种做法仅对非加密的认证方案有效:加密签名需要签署一个新的 nonce。在本文件之前,没有方法能够在不暴露其提供需要认证资源这一事实的情况下与客户端共享此类 nonce。本文件定义了一种新的不可探测的加密认证方案。
本文备忘录的状态
这是一个 Internet 标准轨道文档。
本文档是互联网工程任务组 (IETF) 的产物。它代表了 IETF 社区的共识。它已公开审查并已被互联网工程指导小组 (IESG) 批准发表。有关互联网标准的更多信息,请参阅 RFC 7841 第 2 节。
有关本文档当前状态、任何勘误以及如何对其提供反馈的信息,请访问 https://www.rfc-editor.org/info/rfc9729。
Copyright Notice
Copyright (c) 2025 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 (https://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 Revised BSD License text as described in Section 4.e of the Trust Legal Provisions and are provided without warranty as described in the Revised BSD License.
1. 介绍
HTTP 认证方案(参见 RFC 9110 第 11 节)允许源对部分资源仅限经认证的请求访问。尽管这些方案通常涉及源向客户端发出挑战以要求其提供认证信息,但客户端也可以在未被提示时主动发送此类信息。在源希望仅向“知情者”提供服务或功能而不给其他人任何存在提示的场景中,这种做法尤其有用。此类设计依赖于一种外部定义的密钥分发机制。例如,公司可能通过其网站使用员工凭据直接为远程员工提供公司服务,或为特定员工提供有限的特殊功能,同时使发现(或探测)这些功能变得困难。另一个例子是,定义不那么严格的社区成员可能会使用更短期的密钥以获取地理或能力特定的资源访问,而这些密钥由其用户基数大于可支持资源的实体按时间或地理位置对密钥的可用性进行计量发行。
尽管已经存在基于数字签名的 HTTP 认证方案(例如 [HOBA]),它们依赖于源显式向客户端发送一个新鲜的挑战以确保签名输入是新的。这使得源在向未认证客户端发送挑战时是可探测的。本文档定义了一种新的基于签名且不可探测的认证方案。
1.1. 约定与定义
文中关键字 "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", 和 "OPTIONAL" 的解释,应按 BCP 14(参见 [RFC2119] 与 [RFC8174])中所述,仅在这些词以大写形式出现时按此解释。
本文档使用 RFC 9000 第 1.3 节 中的表示法(QUIC 的相关记法)。
本文档中的若干示例包含可能被折叠的长行,折叠方式参见 [RFC8792]。
2. 隐蔽的 HTTP 认证方案
本文档定义了“Concealed” HTTP 认证方案。它使用非对称加密。客户端持有一个 key ID 及一对公/私钥,源服务器维护已授权 key ID 到相关公钥的映射。
客户端使用 TLS 密钥材料导出器生成要签名的数据(参见 第 3 节),然后使用 Authorization(或 Proxy-Authorization)头字段发送签名(参见 RFC 9110 第 11 节)。签名及其它信息通过认证参数进行交换(参见 第 4 节)。服务器收到这些参数后,可以检查签名是否针对其已知密钥数据库中的某条目验证通过。随后服务器可根据验证结果决定如何响应客户端,例如限制对某些资源的访问。
3. 客户端处理
当客户端希望在请求中使用 Concealed HTTP 认证方案时,SHALL 使用 TLS 密钥材料导出器并按下述参数计算认证证明:
注意,TLS 1.3 的密钥材料导出器定义见 RFC 8446 第 7.5 节,而 TLS 1.2 的导出器定义见 [KEY-EXPORT]。
3.1. 密钥导出器上下文
TLS 密钥导出器上下文在 图 1 中描述,使用来自 RFC 9000 第 1.3 节 的记法:
Signature Algorithm (16), Key ID Length (i), Key ID (..), Public Key Length (i), Public Key (..), Scheme Length (i), Scheme (..), Host Length (i), Host (..), Port (16), Realm Length (i), Realm (..),
图 1:密钥导出器上下文格式
密钥导出器上下文包含下列字段:
- Signature Algorithm:
-
在 s 参数中发送的签名方案(参见 第 4.4 节)。
- Key ID:
-
在 k 参数中发送的 key ID(参见 第 4.1 节)。
- Public Key:
-
服务器用于验证客户端提供签名的公钥。其编码在 第 3.1.1 节 中描述。
- Scheme:
-
请求使用的 scheme,按 URI 的 scheme 部分的格式编码(参见 RFC 3986 第 3.1 节)。
- Host:
-
请求的 host,按 URI 的 host 部分的格式编码(参见 RFC 3986 第 3.2.2 节)。
- Port:
-
请求的端口,按网络字节序编码的端口号。注意端口要么包含在 URI 中,要么为所用 scheme 的默认端口(参见 RFC 3986 第 3.2.3 节)。
- Realm:
-
在 realm 认证参数中发送的认证 realm(参见 RFC 9110 第 11.5 节)。如果未出现 realm 认证参数,则本字段SHALL为空。本文档不定义源向客户端传达 realm 的方式。如果客户端未配置特定 realm,则SHALL使用空 realm 且SHALL NOT发送 realm 认证参数。
Signature Algorithm 与 Port 字段按无符号 16 位整数的网络字节序编码。Key ID、Public Key、Scheme、Host 与 Realm 字段为带长度前缀的字符串;它们前面有一个表示字节长度的 Length 字段。该长度字段使用 RFC 9000 第 16 节 中的可变长度整数编码,并且MUST使用最少字节数进行编码。
3.1.1. 公钥编码
TLS 密钥导出器上下文的 "Public Key" 字段(见上文)与 a 参数(见 第 4.2 节)承载相同的公钥。公钥的编码由所用签名算法决定,如下所示:
- RSASSA-PSS 算法:
- ECDSA 算法:
-
公钥为 UncompressedPointRepresentation 结构,按 RFC 8446 第 4.2.8.2 节 定义,使用 SignatureScheme 指定的曲线。
- EdDSA 算法:
-
公钥为在 [EdDSA] 中定义的字节串编码。
本文档未为其它算法定义公钥编码。若某个 SignatureScheme 要与 Concealed 认证方案配合使用,则其公钥编码需在相应文档中定义。
3.2. 密钥导出器输出
密钥导出器输出为 48 字节。其前 32 字节为签名输入的一部分,随后 16 字节随签名一同发送,供接收方确认导出器生成了正确的值。此结构见 图 2,使用来自 RFC 9000 第 1.3 节 的记法:
Signature Input (256), Verification (128),
图 2:密钥导出器输出格式
密钥导出器输出包含下列字段:
3.3. 签名计算
从密钥导出器输出提取出 Signature Input(参见 第 3.2 节)后,应在其前缀固定数据再进行签名。签名覆盖的数据为下列各项的串联:
-
由八位元 32 (0x20) 重复 64 次组成的字符串
-
上下文字符串 "HTTP Concealed Authentication"
-
作为分隔符的单个 0 字节
-
从密钥导出器输出中提取的 Signature Input(参见 第 3.2 节)
例如,如果 Signature Input 的 32 字节都为 01,则签名覆盖的内容(以十六进制格式)将为:
2020202020202020202020202020202020202020202020202020202020202020 2020202020202020202020202020202020202020202020202020202020202020 48545450205369676E61747572652041757468656E7469636174696F6E 00 0101010101010101010101010101010101010101010101010101010101010101
图 3:签名覆盖示例内容
该静态前缀的目的是减轻若认证非对称密钥被意外跨协议重复使用可能引发的问题(尽管这是被禁止的,参见 第 8 节)。此构造类似于 TLS 1.3 CertificateVerify 消息(参见 RFC 8446 第 4.4.3 节)。
生成的签名随后通过 p 参数发送到服务器(参见 第 4.3 节)。
4. 认证参数
本规范定义以下认证参数。
下列所有字节序列均使用 base64url 编码(参见 RFC 4648 第 5 节),不加引号且不填充。换言之,这些字节序列认证参数的值MUST NOT包含除 ASCII 字母、数字、短横和下划线之外的任何字符。
下述整数以不带负号且不带前导零的方式编码。换言之,该整数认证参数的值MUST NOT包含除数字之外的字符,且除非完整值为 "0",否则MUST NOT以零开头。
使用 [ABNF] 的语法:
concealed-byte-sequence-param-value = *( ALPHA / DIGIT / "-" / "_" ) concealed-integer-param-value = %x31-39 1*4( DIGIT ) / "0"
图 4:认证参数值的 ABNF
4.1. 参数 k
REQUIRED 的 "k"(key ID)参数为标识客户端希望用于认证的密钥的字节序列。后端使用该值在服务器端的已知密钥数据库中定位条目(参见 第 6.3 节)。
4.2. 参数 a
REQUIRED 的 "a"(公钥)参数为字节序列,指定服务器用以验证客户端所提供签名的公钥。这样可以避免密钥混淆问题(参见 [SEEMS-LEGIT])。公钥的编码在 第 3.1.1 节 中描述。
4.3. 参数 p
REQUIRED 的 "p"(proof)参数为字节序列,指定客户端用以证明其持有与其 key ID 匹配凭证的证明。
4.4. 参数 s
REQUIRED 的 "s"(signature scheme)参数为整数,指定用于计算在 p 参数中传输的证明的签名方案。其值为介于 0 到 65535(含)之间的整数,来自 IANA 维护的 "TLS SignatureScheme" 注册表。
4.5. 参数 v
REQUIRED 的 "v"(verification)参数为字节序列,指定客户端提供以证明其持有密钥导出器输出的 verification 值(参见 第 3.2 节)。这样可以避免某些签名方案下某些密钥能够对多个输入生成有效签名的问题(参见 [SEEMS-LEGIT])。
5. 示例
例如,使用 key ID "basement" 且签名算法为 Ed25519 [ED25519] 的客户端可能产生如下头字段:
注意: '\' 行折叠按 RFC 8792
Authorization: Concealed \
k=YmFzZW1lbnQ, \
a=VGhpcyBpcyBh-HB1YmxpYyBrZXkgaW4gdXNl_GhlcmU, \
s=2055, \
v=dmVyaWZpY2F0aW9u_zE2Qg, \
p=QzpcV2luZG93c_xTeXN0ZW0zMlxkcml2ZXJz-ENyb3dkU\
3RyaWtlXEMtMDAwMDAwMDAyOTEtMD-wMC0w_DAwLnN5cw
图 5:示例头字段
6. 服务器处理
在本节中,我们将服务器角色细分为两类:
-
“frontend” 在终止客户端创建的 TLS 或 QUIC 连接的 HTTP 服务器中运行。
-
“backend” 在能够访问已接受的 key 标识符与公钥数据库的 HTTP 服务器中运行。
在大多数部署中,我们预计 frontend 与 backend 角色会在单个 HTTP 源服务器中实现(如 第 3.6 节 所定义)。然而,这些角色也可以拆分,使 frontend 成为 HTTP 网关(如 第 3.7 节 所定义),而 backend 则为 HTTP 源服务器。
6.1. Frontend 处理
如果配置 frontend 去检查 Concealed HTTP 认证方案,它将解析 Authorization(或 Proxy-Authorization)头字段。如果认证方案被设置为 "Concealed",frontend MUST 验证所有必需的认证参数均存在且可按 第 4 节 中定义的方式正确解析。如果任何参数缺失或解析失败,frontend MUST 忽略整个 Authorization(或 Proxy-Authorization)头字段。
然后 frontend 使用这些认证参数中的数据计算密钥导出器输出(见 第 3.2 节)。frontend 随后将该头字段与密钥导出器输出共享给 backend。
6.2. 前端与后端之间的通信
如果 frontend 与 backend 角色在同一台机器上实现,则可以通过简单的函数调用来处理该情况。
如果这些角色分布在两个独立的 HTTP 服务器之间,则 backend 无法直接访问客户端与 frontend 之间 TLS 连接的 TLS 密钥材料导出器,因此 frontend 需要显式地将其发送给 backend。本文档为此定义了 "Concealed-Auth-Export" 请求头字段。Concealed-Auth-Export 头字段的值是一个 Structured Field Byte Sequence(参见 第 3.3.5 节),其中包含 48 字节的密钥导出器输出(见 第 3.2 节),且不包含任何参数。注意 Structured Field Byte Sequences 使用非 URL 安全变体的 base64 进行编码。例如:
注意: '\' 行折叠按 RFC 8792 Concealed-Auth-Export: :VGhpc+BleGFtcGxlIFRMU/BleHBvcn\ Rlc+BvdXRwdXQ/aXMgNDggYnl0ZXMgI/+h:
图 6:示例 Concealed-Auth-Export 头字段
frontend SHALL 将 HTTP 请求转发给 backend,包含原始未修改的 Authorization(或 Proxy-Authorization)头字段以及新添加的 Concealed-Auth-Export 头字段。
注意,由于该机制的安全性依赖于密钥导出器输出的正确性,backend 需要信任 frontend 如实发送该值。该信任关系很常见,因为 frontend 已经需要访问 TLS 证书私钥以响应请求。解析 Concealed-Auth-Export 头字段的 HTTP 服务器 MUST 忽略它,除非它们已确认信任发送者。类似地,发送 Concealed-Auth-Export 头字段的 frontend MUST 确保不转发从客户端接收到的任何 Concealed-Auth-Export 头字段。
6.3. Backend 处理
一旦 backend 收到 Authorization(或 Proxy-Authorization)头字段和密钥导出器输出,它会在其公钥数据库中查找该 key ID。backend SHALL 然后执行下列检查:
如果所有这些检查都通过,backend 可认为该请求已正确认证并据此回复(backend 也可以将请求转发给另一个 HTTP 服务器)。
如果上述任何检查失败,backend MUST 将其视为 Authorization(或 Proxy-Authorization)头字段缺失的情况。
6.4. 不可探测的服务器处理
希望引入不可被探测资源的服务器需要确保不会向未认证的客户端透露关于这些资源的任何信息。具体而言,此类服务器 MUST 在认证失败时返回与资源不存在时完全相同的响应。例如,这可能意味着使用 HTTP 状态码 404(未找到)而非 401(未授权)。
上述认证检查可能需要一些时间来计算,攻击者可以通过比较对已知不存在资源的请求与对可能需要认证的资源的请求的时序差异来探测该机制的使用。服务器可以通过对某些不存在的资源稍作延迟响应来缓解这种可观测性,从而使认证验证的时序不可观测。该延迟需谨慎考虑,以免延迟本身暴露出该源正在使用该机制。
不可探测资源还需要对未认证用户不可发现。例如,如果服务器操作员希望通过对未认证用户假装资源不存在来隐藏某个认证资源,那么操作员需要确保没有未认证页面包含指向该资源的链接,并且没有其它带外方式使未认证用户发现该资源。
7. 关于 TLS 使用的要求
该认证方案仅定义用于带有 TLS 的 HTTP [TLS] 的使用场景。这包括典型用于 HTTP/2 [HTTP/2] 的任意通过 TLS 的 HTTP,或在传输协议使用 TLS 作为其认证与密钥交换机制的 HTTP/3 [HTTP/3] 的用法(参见 [QUIC-TLS])。
由于 TLS 密钥材料导出器仅在其唯一绑定到 TLS 会话时才对认证是安全的(参见 [RFC7627]),Concealed 认证方案要求满足下列任一属性:
客户端 MUST NOT 在不满足上述任一属性的连接上使用 Concealed HTTP 认证方案。如果服务器在既不满足上述条件的连接上收到使用该认证方案的请求,服务器 MUST 将该请求视为未包含认证的请求。
8. 安全注意事项
Concealed HTTP 认证方案允许客户端在保证新鲜性的同时向源服务器进行认证,而无需服务器向客户端传输 nonce。这使得服务器可以在不透露其对某些资源支持或期望认证的情况下接受已认证的客户端。它也允许在不因明文 TLS Client Hello 扩展而向观察者泄露认证存在的情况下进行认证。
由于上述新鲜性由 TLS 密钥导出器提供,其新鲜程度可能与底层 TLS 连接一样旧。服务器可以通过强制客户端使用诸如 GOAWAY 帧(参见 第 5.2 节)等机制来创建新连接,以要求更好的新鲜性。
本文档描述的认证证明并未绑定到单个 HTTP 请求;如果在同一连接上对多个请求使用该密钥进行认证证明,则这些证明将全部相同。这便于在传输时更好地压缩,但意味着将不同安全上下文复用到单个 HTTP 连接的客户端实现需要确保这些上下文不能互相读取对方的头字段。否则,一个上下文可能会重放另一个上下文的 Authorization 头字段。现代浏览器满足此约束。如果攻击者入侵浏览器并能访问另一个上下文的内存,攻击者也可能获取相应密钥,因此将认证绑定到请求在实践中并不会带来太大益处。
用于 Concealed HTTP 认证方案的认证非对称密钥 MUST NOT 在其它协议中重用。尽管我们试图通过在被签名数据前添加静态前缀来缓解该问题(见 第 3.3 节),但重用密钥可能会削弱认证的安全保证。
提供该方案的源可以将使用相同密钥的请求关联在一起。但如果所用密钥是针对各自使用该方案的独立源专门生成的,则请求在跨源之间不可关联。
9. IANA 注意事项
9.1. HTTP 认证方案注册表
IANA 已在 <https://www.iana.org/assignments/http-authschemes> 维护的“HTTP Authentication Schemes”注册表中登记了如下条目:
- Authentication Scheme Name:
- Concealed
- Reference:
- RFC 9729
- Notes:
- 无
9.2. TLS 密钥导出器标签
IANA 已在 <https://www.iana.org/assignments/tls-parameters> 维护的 “TLS Exporter Labels” 注册表中登记了如下条目:
- Value:
- EXPORTER-HTTP-Concealed-Authentication
- DTLS-OK:
- N
- Recommended:
- Y
- Reference:
- RFC 9729
9.3. HTTP 字段名
IANA 已在 <https://www.iana.org/assignments/http-fields> 维护的“Hypertext Transfer Protocol (HTTP) Field Name Registry”中登记了如下条目:
- Field Name:
- Concealed-Auth-Export
- Status:
- permanent
- Structured Type:
- Item
- Reference:
- RFC 9729
- Comments:
- 无
10. 参考文献
10.2. 信息性参考文献
- [ED25519]
- ……
- [HOBA]
- ……
- [HTTP/2]
- ……
- [HTTP/3]
- ……
- [MASQUE-ORIGINAL]
- ……
- [QUIC-TLS]
- ……
- [SEEMS-LEGIT]
- ……
致谢
作者感谢 IETF 社区的众多成员,本文件是许多走廊讨论的结晶。尤其感谢 David Benjamin、Reese Enghardt、Nick Harper、Dennis Jackson、Ilari Liusvaara、François Michel、Lucas Pardue、Justin Richer、Ben Schwartz、Martin Thomson 与 Chris A. Wood 的审阅与贡献。本文档中描述的机制最初是 MASQUE 的第一版的一部分 [MASQUE-ORIGINAL]。