跳到主要内容

7. Protected Resource Access (受保护资源访问)

7. Protected Resource Access (受保护资源访问)

对 DPoP 受保护资源的请求必须同时包括第 4 节所述的 DPoP 证明和第 7.1 节所述的访问令牌。DPoP 证明必须包含 ath 声明,其中包含关联访问令牌的有效哈希值。

以这种方式将令牌值绑定到证明,可以防止一个证明在不同请求中与多个不同的访问令牌值一起使用。例如,如果客户端持有绑定到两个不同资源所有者(AT1 和 AT2)的令牌,并且在与授权服务器通信时使用相同的密钥,则这些令牌可能会被交换。如果没有 ath 字段进行绑定,应用于 AT1 的捕获签名可能会被重放给 AT2,从而改变预期请求的权限和访问权限。这种防止替换的措施对于同一客户端和资源所有者组合内的轮换访问令牌仍然有效——轮换的令牌值将需要计算新的证明。这种绑定还确保了旨在与访问令牌一起使用的证明在没有访问令牌的情况下无法使用,反之亦然。

资源服务器需要计算出示的令牌值的哈希,并验证它是否与 ath 字段中的哈希值相同,如第 4.3 节所述。由于 ath 字段值被 DPoP 证明的签名覆盖,因此包含该字段会将访问令牌值绑定到用于生成签名的密钥的持有者。

请注意,仅凭 ath 字段并不能防止 DPoP 证明的重放,也不能提供与出示证明的请求的绑定,因此检查证明的时间窗口以及包含的消息参数(如 htm 和 htu)仍然很重要。

7.1. DPoP 身份验证方案

根据 [RFC9110] 第 11.6.2 节,DPoP 绑定的访问令牌使用 Authorization 请求头字段发送,身份验证方案为 DPoP。DPoP 方案的 Authorization 头字段的语法使用 [RFC9110] 第 11.2 节中定义的 token68 语法作为凭据,为了便于参考,在下面重复该语法。DPoP 身份验证方案凭据的 ABNF 标记法语接如下:

token68    = 1*( ALPHA / DIGIT /
"-" / "." / "_" / "~" / "+" / "/" ) *"="

credentials = "DPoP" 1*SP token68

图 12: DPoP 身份验证方案 ABNF

对于此类访问令牌,资源服务器必须检查是否还在 HTTP 请求的 DPoP 头字段中收到了 DPoP 证明,根据第 4.3 节中的规则检查 DPoP 证明,并根据第 6 节检查 DPoP 证明的公钥是否与访问令牌绑定的公钥匹配。

除非所有检查都成功,否则资源服务器不得授予对资源的访问权限。

图 13 显示了对受保护资源的示例请求,其中 Authorization 头部包含 DPoP 绑定访问令牌,DPoP 头部包含 DPoP 证明。该示例根据 [RFC8792] 使用 "" 换行。图 14 显示了该 DPoP 证明的解码内容。显示了 JWT 头部和有效负载的 JSON,但省略了签名部分。像往常一样,为了格式化和可读性,包含了换行符和缩进。

GET /protectedresource HTTP/1.1
Host: resource.example.org
Authorization: DPoP Kz~8mXK1EalYznwH-LC-1fBAo.4Ljp~zsPE_NeO.gxU
DPoP: eyJ0eXAiOiJkcG9wK2p3dCIsImFsZyI6IkVTMjU2IiwiandrIjp7Imt0eSI6Ik\
VDIiwieCI6Imw4dEZyaHgtMzR0VjNoUklDUkRZOXpDa0RscEJoRjQyVVFVZldWQVdCR\
nMiLCJ5IjoiOVZFNGpmX09rX282NHpiVFRsY3VOSmFqSG10NnY5VERWclUwQ2R2R1JE\
QSIsImNydiI6IlAtMjU2In19.eyJqdGkiOiJlMWozVl9iS2ljOC1MQUVCIiwiaHRtIj\
oiR0VUIiwiaHR1IjoiaHR0cHM6Ly9yZXNvdXJjZS5leGFtcGxlLm9yZy9wcm90ZWN0Z\
WRyZXNvdXJjZSIsImlhdCI6MTU2MjI2MjYxOCwiYXRoIjoiZlVIeU8ycjJaM0RaNTNF\
c05yV0JiMHhXWG9hTnk1OUlpS0NBcWtzbVFFbyJ9.2oW9RP35yRqzhrtNP86L-Ey71E\
OptxRimPPToA1plemAgR6pxHF8y6-yqyVnmcw6Fy1dqd-jfxSYoMxhAJpLjA

图 13: DPoP 受保护资源请求
{
"typ":"dpop+jwt",
"alg":"ES256",
"jwk": {
"kty":"EC",
"x":"l8tFrhx-34tV3hRICRDY9zCkDlpBhF42UQUfWVAWBFs",
"y":"9VE4jf_Ok_o64zbTTlcuNJajHmt6v9TDVrU0CdvGRDA",
"crv":"P-256"
}
}
.
{
"jti":"e1j3V_bKic8-LAEB",
"htm":"GET",
"htu":"https://resource.example.org/protectedresource",
"iat":1562262618,
"ath":"fUHyO2r2Z3DZ53EsNrWBb0xWXoaNy59IiKCAqksmQEo"
}

图 14: 图 13 中 DPoP 证明 JWT 的解码内容

收到对保护空间内需要 DPoP 身份验证的受保护资源的请求后,如果请求不包含有效凭据或不包含足以访问的访问令牌,服务器可以响应向客户端发出的质询,要求提供 DPoP 身份验证信息。此类质询使用 401 (Unauthorized) 响应状态代码 ([RFC9110], 第 15.5.2 节) 和 WWW-Authenticate 头字段 ([RFC9110], 第 11.6.1 节) 发出。服务器也可以针对其他条件在响应中包含 WWW-Authenticate 头部。

在此类质询中:

  • 方案名称为 DPoP。
  • 可以包含身份验证参数 realm,以按照 [RFC9110] 第 11.5 节中描述的方式指示保护范围。
  • 可以包含 [RFC6750] 第 3 节中定义的 scope 身份验证参数。
  • 如果请求包含访问令牌但身份验证失败,则应该包含 error 参数 ([RFC6750], 第 3 节) 以指示请求被拒绝的原因。[RFC6750] 第 3.1 节中描述的 error 参数值是合适的,扩展定义的任何适当值也是如此。可以使用值 use_dpop_nonce 如第 9 节所述,以表明后续请求的 DPoP 证明中需要 nonce。此外,使用 invalid_dpop_proof 表示根据第 4.3 节的标准,DPoP 证明本身被视为无效。
  • error_description 参数 ([RFC6750], 第 3 节) 可以与 error 参数一起包含,以便为开发人员提供不打算显示给最终用户的可读解释。
  • 应该包含 algs 参数,以向客户端表明 DPoP 证明 JWT 可接受的 JWS 算法。该参数的值是以空格分隔的 JWS alg (Algorithm) 头部值列表 ([RFC7515], 第 4.1.1 节)。
  • 可以使用其他身份验证参数,接收者必须忽略未知参数。

图 15 显示了对没有身份验证的受保护资源请求的响应。

HTTP/1.1 401 Unauthorized
WWW-Authenticate: DPoP algs="ES256 PS256"

图 15: 对没有身份验证的受保护资源请求的 HTTP 401 响应

图 16 显示了对受保护资源请求的响应,该请求由于访问令牌中 DPoP 绑定的确认失败而被拒绝。图 16 根据 [RFC8792] 使用 "" 换行。

HTTP/1.1 401 Unauthorized
WWW-Authenticate: DPoP error="invalid_token", \
error_description="Invalid DPoP key binding", algs="ES256"

图 16: 对带有无效令牌的受保护资源请求的 HTTP 401 响应

请注意,默认情况下,使用跨域资源共享 (CORS) [WHATWG.Fetch] 的基于浏览器的客户端应用程序只能访问 CORS 安全列表响应 HTTP 头部。为了使应用程序获得并使用 WWW-Authenticate HTTP 响应头值,服务器需要通过在 Access-Control-Expose-Headers 响应头列表值中包含 WWW-Authenticate 使其可供应用程序使用。

此身份验证方案仅用于源服务器身份验证。因此,此身份验证方案不得与 Proxy-Authenticate 或 Proxy-Authorization 头字段一起使用。

请注意,此身份验证方案的Authorization头字段的语法遵循 [RFC6750] 第 2.1 节中定义的 Bearer 方案的用法。虽然它不是 [RFC9110] 的首选凭证语法,但它与其中的一般身份验证框架兼容,并且用于保持一致性和与 Bearer 方案的熟悉度。

7.2. 与 Bearer 身份验证方案的兼容性

同时支持 DPoP 和 Bearer 方案的受保护资源需要更新对 bearer 令牌执行评估过程的方式,以防止降级使用 DPoP 绑定的访问令牌。具体来说,此类受保护资源必须根据 [RFC6750] 拒绝作为 bearer 令牌接收的 DPoP 绑定访问令牌。

[RFC9110] 的第 11.6.1 节允许受保护资源通过 401 (Unauthorized) 响应的 WWW-Authenticate 头字段指示对多种身份验证方案(即 Bearer 和 DPoP)的支持。

仅支持 [RFC6750] 且不知道 DPoP 的受保护资源很可能会接受 DPoP 绑定的访问令牌作为 bearer 令牌(JWT [RFC7519] 说要忽略无法识别的声明,内省 [RFC7662] 说虽然对其存在没有功能要求,但可能存在其他参数,并且 [RFC6750] 实际上对访问令牌的内容保持沉默,因为它与有效性有关)。因此,收到来自受保护资源的 WWW-Authenticate: Bearer 质询后,客户端可以使用 Bearer 方案发送 DPoP 绑定的访问令牌(或者如果它事先知道受保护资源的功能,它可以发送 DPoP 绑定的访问令牌)。这样做的效果可能会简化受保护资源分阶段升级以支持 DPoP 或长期部署支持混合令牌类型的受保护资源的后勤工作。

如果支持 Bearer 和 DPoP 方案的受保护资源选择响应多个 WWW-Authenticate 质询,则应注意哪个质询应传递实际的错误信息。建议遵守以下规则:

  • 如果请求中未包含身份验证信息,则质询不应包含错误代码或其他错误信息,根据 [RFC6750] 第 3.1 节(图 17)。

  • 如果可以明确建立用于尝试身份验证的机制,则应使用相应的质询来传递错误信息(图 18)。

  • 否则,可以使用 Bearer 和 DPoP 质询来传递错误信息(图 19)。

以下示例根据 [RFC8792] 使用 "" 换行。

GET /protectedresource HTTP/1.1
Host: resource.example.org

HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer, DPoP algs="ES256 PS256"

图 17: 对没有身份验证的受保护资源请求的 HTTP 401 响应
GET /protectedresource HTTP/1.1
Host: resource.example.org
Authorization: Bearer INVALID_TOKEN

HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer error="invalid_token", \
error_description="Invalid token", DPoP algs="ES256 PS256"

图 18: 对身份验证无效的受保护资源请求的 HTTP 401 响应
GET /protectedresource HTTP/1.1
Host: resource.example.org
Authorization: Bearer Kz~8mXK1EalYznwH-LC-1fBAo.4Ljp~zsPE_NeO.gxU
Authorization: DPoP Kz~8mXK1EalYznwH-LC-1fBAo.4Ljp~zsPE_NeO.gxU

HTTP/1.1 400 Bad Request
WWW-Authenticate: Bearer error="invalid_request", \
error_description="Multiple methods used to include access token", \
DPoP algs="ES256 PS256", error="invalid_request", \
error_description="Multiple methods used to include access token"

图 19: 对身份验证不明确的受保护资源请求的 HTTP 400 响应

7.3. 客户端注意事项

包含 DPoP 证明的授权可能不是幂等的(取决于服务器对 jti、iat 和 nonce 声明的强制执行)。因此,以前对受保护资源的所有幂等请求可能不再是幂等的。建议客户端生成唯一的 DPoP 证明,即使在响应通常被理解为暂时的 HTTP 错误而重试幂等请求时也是如此。

遇到频繁网络错误的客户端在与实施更严格 nonce 验证的服务器交互时可能会遇到额外的挑战。