在基于 OAuth2 的身份验证中,状态参数可以防止啥样的 CSRF 攻击?

Posted

技术标签:

【中文标题】在基于 OAuth2 的身份验证中,状态参数可以防止啥样的 CSRF 攻击?【英文标题】:What kind of CSRF attack does state parameter prevent in OAuth2-based authentication?在基于 OAuth2 的身份验证中,状态参数可以防止什么样的 CSRF 攻击? 【发布时间】:2020-03-08 10:49:24 【问题描述】:

我正在使用 Google OAuth2 API 进行身份验证。 我使用的是“服务器”流,而不是“隐式”。

在执行获取code 的步骤1 时,指南建议使用state 参数,以确保服务提供者最终会收到与其发起的身份验证请求相关的响应。

见https://developers.google.com/identity/protocols/OpenIDConnect#createxsrftoken

我了解code 有可能被盗,redirect_uri 猜测。

但我不明白为什么这被称为反 CSRF 保护?

根据wiki CSRF 攻击“利用网站在用户浏览器中的信任”。 据我了解,应该在浏览器中保留一些敏感内容,以使 CSRF 攻击成为可能。最经典的例子 - 身份验证 cookie。

但是与 OpenID-connect 代码流相关的浏览器中保留了什么? 这只是从服务提供者到身份提供者并返回的两个后续重定向。 代码本身作为 URL 参数在回调中传递。浏览器的角色是被动的 - 被重定向两次。这里没有存储任何内容。

所以我的问题是state 到底能防止什么样的 CSRF 攻击?谁能给个详细的例子?还是只是在谷歌指南中滥用了“CSRF”一词?

【问题讨论】:

只是一个有用的想法:这不仅仅是为了防止当前的漏洞利用 - 它还可以安全地防范尚未考虑过的未来漏洞。我自己也无法想象有什么真正糟糕的事情,但总会有人发明一些真正有创意的攻击——如果有足够的时间经验表明几乎所有事情都会以某种方式受到攻击。例如:让您以某种方式重新授权,以便他们可以获取您的详细信息等。 【参考方案1】:

在 OAuth2 中使用授权码流时,用户浏览到客户端应用程序,然后将浏览器重定向到授权服务器进行登录并获取访问令牌。

302 https://auth.server/authorize?response_type=code&client_id=....

在您登录授权服务器后,它会将您重定向回已注册的重定向 URI,并使用发布的代码。然后,客户端应用程序将代码交换为访问令牌,并可选择转到在 state 参数中编码的 URL。

现在,攻击者可能会诱骗您点击链接(来自电子邮件或其他内容),例如:

<a href="https://auth.server/authorize?response_type=code&client_id=...."

这将对授权服务器执行相同的请求。一旦您登录并且授权服务器将您重定向回客户端应用程序,客户端应用程序无法知道带有授权代码的传入 GET 请求是由它发起的 302 重定向还是由您单击该超链接引起的恶意电子邮件。

客户端应用程序可以通过在授权请求的状态参数中添加一些熵来防止这种情况。由于状态参数将作为最终重定向回客户端应用程序的查询参数包含在内,因此客户端应用程序可以检查熵是否与它在本地(或仅在安全的 HTTP cookie 中)保存的熵相匹配,并确定它启动了授权请求,因为攻击者无法在 state 参数中创建一个带有熵的 URL,以匹配客户端应用程序保留的内容。

【讨论】:

很好解释,很好的例子,谢谢!虽然我仍然缺少最后一部分 - 攻击者究竟可以将什么放入格式错误的链接中以使用户做一些有害的事情?我只是没有看到任何空间。 redirect_uri 是硬编码且精确的,任何尝试使用它都会导致“重定向 uri 不匹配”。此外,最后一个重定向是 GET 而不是状态更改。另一方面,如果攻击者在state 中对可选uri 进行了畸形处理,那么这对我来说是个悖论——state 参数提供了一个攻击面,同时又阻止了它 当然,如果您的站点遵循 REST 原则,则不应通过 GET 请求更改状态。但是,并非所有网站都遵循这些准则。此外,如果站点支持从 state 参数重定向到 URL,它可能容易受到开放重定向攻击,从而使攻击者可以选择从攻击者控制的域加载任何页面。 fyrkov:这也是未来的安全设计。现在,我们谁都无法想象他们能做什么,但坏人真的很有创意。这只是让他们更难通过我们的一层。【参考方案2】:

CSRF 的目的是通过点击攻击者发送的链接来欺骗用户在网站中执行操作(通常是用户在正常情况下不会执行的破坏性写入操作)。此类活动的一个示例可能是在网站中删除用户自己的帐户。假设用户已登录网站,来自用户浏览器的请求会受到网站服务器的信任,而网站服务器无法发现(没有 CSRF 令牌)用户实际上是被欺骗发出该请求的。

如果是 Google OAuth2(授权码授予类型),请注意,对 Google 身份验证服务器的初始请求包含用户在身份验证成功后实际想要访问的 url。攻击者可以出于某种恶意小心地构造该 url 并让用户使用它。

状态令牌检查使服务器能够确保请求确实来自其自己的网站,而不是来自任何其他地方。如果攻击者使用一些随机状态令牌创建 url,服务器将无法识别并拒绝请求。

【讨论】:

“对 Google auth 服务器的初始请求包含用户想要访问的 url”是指redirect_uri 吗?如果是这样,您能否解释一下,如果在 Google 端检查它是否存在于允许的重定向 uri 列表中,如何伪造 uri? @fyrkov 不是redirect_uri。我的意思是url%3Dhttps://oauth2-login-demo.example.com/myHome 是具体的 好的,现在我明白了。但是这个所需的 uri 是 state 参数的一部分,它本身是可选的,对吗?如果我们作为服务提供者的实现者选择忽略state,那么这种攻击就不可能了,对吗? @fyrkov 无法理解这个问题。必须存在状态参数以防止 CSRF。该参数可能包含也可能不包含 url - 取决于您的实现方式。文档中提到了两种可能的实现 - 加密强随机数或使用某些状态变量生成的哈希,这些状态变量可能包含也可能不包含 uri【参考方案3】:

如果您有这样的疑问,您必须参考的最佳资源是规范。对于 OAuth 2.0,这是 RFC6749 。规范中有专门的部分讨论Cross-Site Request Forgery

跨站请求伪造 (CSRF) 是一种攻击者利用 导致受害者最终用户的用户代理遵循恶意 URI (例如,作为误导性链接、图像提供给用户代理,或 重定向)到信任服务器(通常通过 存在有效的会话 cookie)。

从规范的角度来看,您必须在您的应用程序中实现状态处理机制

客户端必须为其重定向 URI 实施 CSRF 保护。 这通常通过要求将任何请求发送到 重定向 URI 端点以包含将请求绑定到的值 用户代理的身份验证状态(例如,会话的哈希 cookie 用于验证用户代理)。客户应该 利用“状态”请求参数将此值传递给 发出授权请求时的授权服务器。

关于详细的解释,直接来自规范

针对客户端重定向 URI 的 CSRF 攻击允许攻击者 注入自己的授权码或访问令牌,可以 导致客户端使用与 攻击者的受保护资源,而不是受害者的

注入来自攻击者的授权代码,然后操纵您的应用程序以攻击者想要的方式运行。

【讨论】:

以上是关于在基于 OAuth2 的身份验证中,状态参数可以防止啥样的 CSRF 攻击?的主要内容,如果未能解决你的问题,请参考以下文章

Spring boot,Security,OAuth2:是不是可以使用自定义 AuthorizationCodeResourceDetails?身份验证服务器需要重定向 url 中的特定参数

如何有单独的身份验证源? (一个用于 Oauth2,一个用于基于表单的登录)

一个应用程序中的 Spring Security OAuth2 身份验证和表单登录

如果没有 OpenID/Oauth2 等广泛使用的标准,我可以只使用 JWT 进行身份验证吗?

如何基于使用 Oauth2 协议的身份验证改进 JWT 访问令牌和刷新令牌?

Springboot + JWT +OAuth2 + AngularJS 无状态会话