跳到主要内容

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 证明,接收服务器必须确保以下几点:

  1. DPoP HTTP 请求头字段不超过一个.
  2. DPoP HTTP 请求头字段值是一个单一且格式正确的 JWT.
  3. JWT 中包含第 4.2 节要求的所有声明.
  4. typ JOSE 头部参数的值为 dpop+jwt.
  5. alg JOSE 头部参数指示已注册的非对称数字签名算法 [IANA.JOSE.ALGS],不是 none,受应用程序支持,并且根据本地策略是可接受的.
  6. JWT 签名使用 jwk JOSE 头部参数中包含的公钥进行验证.
  7. jwk JOSE 头部参数不包含私钥.
  8. htm 声明与当前请求的 HTTP 方法匹配.
  9. htu 声明与接收到 JWT 的 HTTP 请求的 HTTP URI 值匹配,忽略任何查询和片段部分.
  10. 如果服务器向客户端提供了 nonce 值,则 nonce 声明与服务器提供的 nonce 值匹配.
  11. JWT 的创建时间(由 iat 声明或服务器通过 nonce 声明管理的时间戳确定)在一个可接受的窗口内(参见第 11.1 节).
  12. 如果结合访问令牌向受保护资源提交,
    • 确保 ath 声明的值等于该访问令牌的哈希值,并且
    • 确认为访问令牌绑定的公钥与 DPoP 证明中的公钥匹配.

为了降低误报的可能性,服务器应该在比较 htu 声明之前采用基于语法的规范化([RFC3986] 第 6.2.2 节)和基于方案的规范化([RFC3986] 第 6.2.3 节).

可以按任何顺序执行这些检查.