我正在学习有关 OpenID Connect 授权代码工作流程的 ASP.NET 安全课程,该课程具有针对代码重放攻击的 PKCE 保护。这个过程有一个方面我不明白。
流程:
依赖客户端生成PKCE code_verifier,将其哈希为code_challenge,并将code_challenge作为查询参数将用户发送到授权服务器。
授权服务器存储code_challenge,发出授权码,并使用授权码将用户重定向回客户端。
客户端将授权码与原始code_verifier一起发送以换取令牌。授权服务器在发出令牌之前验证 code_verifier 确实散列到 code_challenge 中。
我的问题是步骤 #3:由于 HTTP 是无状态的,客户端如何知道要与授权代码一起发送哪个 code_verifier?此 code_verifier 是否存储在用户代理上的 cookie 中?
使用 cookie 来存储“代码验证器”似乎违反了 OpenId 规范的精神,而且似乎是一个很大的安全漏洞。
简而言之,规范希望您使用 S256 对代码验证器进行哈希处理,并且使代码验证器保持秘密,以免被窃听或被猜测。
PKCE RFC 第 7.1 节 表示“安全模型依赖于代码验证者没有被攻击者学习或猜测的事实。遵守这一原则至关重要。因此,代码验证者必须以加密随机且具有高熵的方式创建,攻击者无法猜测。” ...“使用“S256”可以防止向攻击者泄露“code_verifier”值。”
现在,如果您加密“代码验证器”,它确实可以使用 cookie。这是规范中所说的内容 第7.2节:“如果代码质询方法是“普通”并且代码质询要在授权“代码”内返回以实现无状态服务器,则必须以只有服务器可以解密的方式对其进行加密并提取它。”
嗯,OpenId 规范有一个解决方案,微软也提供了一个可以遵循的实现。调用“授权”端点时请参考OpenId规范中的“state”参数。身份提供者会将值返回给调用者。
在 .NET Core 中完成的方式是使用“state”查询字符串参数,其中值是受保护(加密)的字典。如需帮助,请查看 .NET Core 代码并查看“Microsoft.AspNetCore.Authentication.OAuth.OAuthHandler”中的方法“BuildChallengeUrl”
此外,这个网站有一个行之有效的好主意。这是一个有效的选项。
PKCE 最初适用于移动客户端,通常仅发出单个授权请求。但现在建议所有使用授权码流程的客户。
您是对的,如果客户端是为许多用户提供服务的 Web 应用程序,则客户端应用程序需要将正确的 code_verifier 与授权请求关联起来。
正如你所说,客户端可以将code_verifier存储在cookie中,因此它将与授权码一起发送给客户端。
或者它可以将其存储在客户端的服务器端,并将状态参数中的密钥粘贴到授权请求中。如果与授权请求一起传递,状态将成为对 redirect_uri 的调用的一部分。
我最近写了一篇文章正是针对这一点。
在 Duende Identity Server(使用 aspnetcore 代码)中,当充当客户端时,
code_verifier
存储在 state
查询字符串参数中。
收到
/authorize
请求时(在将请求转发到外部 IdP 之前),它会执行以下操作:
code_verifier
并将其放入 state
中,并加密 state
。state
以及 code_challenge
和 code_challenge_method
作为重定向发送回浏览器,以便浏览器可以将其转发给 IdP。state
。state
,并从中提取 code_verifier
。code_verifier
以及其他必要的参数(clientID、客户端密钥等)发送到 IdP。