4. DPoP Proof JWTs (DPoP 证明 JWT)
4. DPoP Proof JWTs (DPoP 证明 JWT)
DPoP 引入了 DPoP 证明 (DPoP proof) 的概念,它是由客户端创建并使用 DPoP 头部字段随 HTTP 请求一起发送的 JWT。每个 HTTP 请求都需要一个唯一的 DPoP 证明。
有效的 DPoP 证明向服务器证明客户端持有用于签署 DPoP 证明 JWT 的私钥。这使授权服务器能够将颁发的令牌绑定到相应的公钥(如第 5 节所述),并使资源服务器能够验证其接收到的令牌的密钥绑定(参见第 7.1 节),从而防止上述令牌被任何无法访问私钥的实体使用。
DPoP 证明演示了密钥的所有权,其本身不是身份验证或访问控制机制。当如第 7.1 节所述结合密钥绑定的访问令牌出示时,DPoP 证明为客户端出示访问令牌的合法性提供了额外的保证。但是,有效的 DPoP 证明 JWT 本身不足以做出访问控制决策。
4.1. DPoP HTTP 头部
DPoP 证明包含在 HTTP 请求中,使用以下请求头字段。
DPoP: 遵循第 4.2 节结构和语法的 JWT。
图 2 显示了一个 DPoP HTTP 头字段的示例。根据 [RFC8792],该示例使用 "" 换行。
DPoP: eyJ0eXAiOiJkcG9wK2p3dCIsImFsZyI6IkVTMjU2IiwiandrIjp7Imt0eSI6Ik\
VDIiwieCI6Imw4dEZyaHgtMzR0VjNoUklDUkRZOXpDa0RscEJoRjQyVVFVZldWQVdCR\
nMiLCJ5IjoiOVZFNGpmX09rX282NHpiVFRsY3VOSmFqSG10NnY5VERWclUwQ2R2R1JE\
QSIsImNydiI6IlAtMjU2In19.eyJqdGkiOiItQndDM0VTYzZhY2MybFRjIiwiaHRtIj\
oiUE9TVCIsImh0dSI6Imh0dHBzOi8vc2VydmVyLmV4YW1wbGUuY29tL3Rva2VuIiwia\
WF0IjoxNTYyMjYyNjE2fQ.2-GxA6T8lP4vfrg8v-FdWP0A0zdrj8igiMLvqRMUvwnQg\
4PtFLbdLXiOSsX0x7NVY-FNyJK70nfbV37xRZT3Lg
图 2: DPoP 头部示例
请注意,根据 [RFC9110],头字段名称不区分大小写;因此,DPoP、DPOP、dpop 等都是有效且等效的头字段名称。但是,头字段值中的大小写很重要。
DPoP HTTP 头字段值使用 [RFC9110] 第 11.2 节中定义的 token68 语法,为了便于参考,在下面的图 3 中重复该语法。
DPoP = token68
token68 = 1*( ALPHA / DIGIT /
"-" / "." / "_" / "~" / "+" / "/" ) *"="
图 3: DPoP 头字段 ABNF
4.2. DPoP 证明 JWT 语法
DPoP 证明是一个 JWT [RFC7519],它使用客户端选择的私钥(见下文)进行签名(使用 JSON Web 签名 (JWS) [RFC7515])。DPoP JWT 的 JOSE 头部必须至少包含以下参数:
typ: 一个值为 dpop+jwt 的字段,根据 [RFC8725] 第 3.11 节的建议,它显式地确定了 DPoP 证明 JWT 的类型。
alg: 来自 [IANA.JOSE.ALGS] 的 JWS 非对称数字签名算法的标识符。它不得为 none 或对称算法(消息身份验证码 (MAC))的标识符。
jwk: 表示客户端在 [RFC7515] 第 4.1.3 节定义的 JSON Web Key (JWK) [RFC7517] 格式中选择的公钥。它不得包含私钥。
DPoP 证明的有效负载必须至少包含以下声明:
jti: DPoP 证明 JWT 的唯一标识符。必须分配该值,以使得在有效期的时间窗口内,将相同值分配给同一上下文中使用的任何其他 DPoP 证明的概率可以忽略不计。这种唯一性可以通过编码(base64url 或任何其他合适的编码)至少 96 位伪随机数据或使用符合 [RFC4122] 的版本 4 通用唯一标识符 (UUID) 字符串来实现。服务器可以使用 jti 来进行重放检测和预防;参见第 11.1 节。
htm: 附加了 JWT 的请求的 HTTP 方法的值 ([RFC9110] 第 9.1 节)。
htu: 附加了 JWT 的请求的 HTTP 目标 URI ([RFC9110] 第 7.1 节),不包含查询和片段部分。
iat: JWT 的创建时间戳 ([RFC7519] 第 4.1.6 节)。
当 DPoP 证明与受保护资源访问中的访问令牌出示结合使用时(参见第 7 节),DPoP 证明还必须包含以下声明:
ath: 访问令牌的哈希。该值必须是对关联访问令牌值的 ASCII 编码的 SHA-256 [SHS] 哈希进行 base64url 编码(如 [RFC7515] 第 2 节中所定义)的结果。
当身份验证服务器或资源服务器在响应中提供 DPoP-Nonce HTTP 头部时(参见第 8 节和第 9 节),DPoP 证明还必须包含以下声明:
nonce: 通过 DPoP-Nonce HTTP 头部提供的最近的 nonce。
根据扩展、配置文件或特定于部署的要求,DPoP 证明可以包含其他 JOSE 头部参数或声明。
图 4 是一个概念性示例,显示了图 2 中 DPoP 证明的解码内容。显示了 JWT 头部和有效负载的 JSON,但省略了签名部分。像往常一样,为了格式化和可读性,包含了换行符和额外的空格。
{
"typ":"dpop+jwt",
"alg":"ES256",
"jwk": {
"kty":"EC",
"x":"l8tFrhx-34tV3hRICRDY9zCkDlpBhF42UQUfWVAWBFs",
"y":"9VE4jf_Ok_o64zbTTlcuNJajHmt6v9TDVrU0CdvGRDA",
"crv":"P-256"
}
}
.
{
"jti":"-BwC3ESc6acc2lTc",
"htm":"POST",
"htu":"https://server.example.com/token",
"iat":1562262616
}
图 4: DPoP 证明的 JWT 内容示例
实际上,HTTP 请求中只有 HTTP 方法和 URI 包含在 DPoP JWT 中;因此,DPoP 证明仅覆盖这两个消息部分。其想法是只对足够的 HTTP 数据进行签名,以便为 HTTP 请求提供合理的所有权证明。这种仅使用 HTTP 头部数据的最小子集的设计方法是为了避免尝试规范化 HTTP 消息时固有的巨大困难。尽管如此,DPoP 证明可以扩展为包含 HTTP 请求的其他信息(另请参见第 11.7 节)。
4.3. 检查 DPoP 证明
为了验证 DPoP 证明,接收服务器必须确保以下几点:
- DPoP HTTP 请求头字段不超过一个。
- DPoP HTTP 请求头字段值是一个单一且格式正确的 JWT。
- JWT 中包含第 4.2 节要求的所有声明。
- typ JOSE 头部参数的值为 dpop+jwt。
- alg JOSE 头部参数指示已注册的非对称数字签名算法 [IANA.JOSE.ALGS],不是 none,受应用程序支持,并且根据本地策略是可接受的。
- JWT 签名使用 jwk JOSE 头部参数中包含的公钥进行验证。
- jwk JOSE 头部参数不包含私钥。
- htm 声明与当前请求的 HTTP 方法匹配。
- htu 声明与接收到 JWT 的 HTTP 请求的 HTTP URI 值匹配,忽略任何查询和片段部分。
- 如果服务器向客户端提供了 nonce 值,则 nonce 声明与服务器提供的 nonce 值匹配。
- JWT 的创建时间(由 iat 声明或服务器通过 nonce 声明管理的时间戳确定)在一个可接受的窗口内(参见第 11.1 节)。
- 如果结合访问令牌向受保护资源提交,
- 确保 ath 声明的值等于该访问令牌的哈希值,并且
- 确认为访问令牌绑定的公钥与 DPoP 证明中的公钥匹配。
为了降低误报的可能性,服务器应该在比较 htu 声明之前采用基于语法的规范化([RFC3986] 第 6.2.2 节)和基于方案的规范化([RFC3986] 第 6.2.3 节)。
可以按任何顺序执行这些检查。