OAuth2.0 Server stack如何使用状态来防止CSRF?对于draft2.0 v20

Posted

技术标签:

【中文标题】OAuth2.0 Server stack如何使用状态来防止CSRF?对于draft2.0 v20【英文标题】:OAuth2.0 Server stack how to use state to prevent CSRF? for draft2.0 v20 【发布时间】:2012-06-19 18:02:46 【问题描述】:

我正在使用php library for OAuth2.0 v20

在draft20中,有一个mention of the use of state to prevent CSRF

到目前为止,我自己实现这个 PHP 库的网络应用程序允许以下内容:

    3 legged authentication using Authorization Code Request 2 legged authentication using Resource Owner Credentials Grant a Request that refreshes an access token

我需要对以上 3 种情况都使用状态吗?

如果是这样,什么是“状态”的好例子?

什么是好的“状态”?

任何理想的长度?有最小长度吗?有最大长度吗?

有理想的妆容吗?包括大写的字母数字?

【问题讨论】:

【参考方案1】:

通过一个示例 CSRF 漏洞利用来了解状态参数如何减轻此类攻击可能会有所帮助。在此示例中,Mallory攻击者Alice受害者

攻击

    Mallory 访问某些客户的网站并开始授权该客户使用 OAuth 访问某些服务提供商

    客户请求服务提供商代表 Mallory 请求访问权限,该权限被授予

    Mallory 被重定向到服务提供商的网站,她通常会在该网站输入她的用户名/密码以授权访问

    相反,Mallory 会捕获/阻止此请求并保存其 URL

    现在,Mallory 以某种方式让 Alice 访问该 URL。如果 Alice 使用她自己的帐户登录到服务提供商,那么 她的凭据 将用于颁发 授权码

    授权码换取访问令牌

    现在 Mallory 的 在客户端的帐户被授权访问 Alice 的 在服务提供商的帐户

那么,我们如何使用state 参数来防止这种情况发生?

预防

    客户端应该创建一个基于原始用户帐户的值(例如,用户会话密钥的哈希)。它是什么并不重要,只要它是唯一的并且是使用一些关于原始用户的私人、不可猜测的信息生成的。

    此值在上述第三步的重定向中传递给服务提供者

    现在,当 Mallory 让 Alice 访问保存的 URL 时(上面的第五步),该 URL 包括使用 Mallory 的会话信息生成的 state 参数

    授权码在 Alice 的 会话中发出并发送回客户端连同 Mallory 的 state 参数

    客户端根据Alice 的 会话信息生成一个新的state 值,并将其与从授权请求发送回服务提供商的state 值进行比较。此值与请求中的state 参数不匹配,因为state 值是根据Mallory 的 会话信息生成的,因此被拒绝。

攻击者不应为任何特定用户生成状态值,因此,诱骗用户访问其授权 URL 无效。

【讨论】:

当我的应用程序的回调 URL 被调用时(由于来自服务提供商的重定向),我查看会话 cookie(实际上是一个包含用户 ID 的加密 cookie)并知道访问是针对哪个用户的确。为什么在这种情况下我需要 state 参数? 解释清楚,干得好 :) 似乎很好解释。也许你可以说 mallory 如何给 alice url 以及 alice 如何给 mallory 授权码(她的客户端实例应该请求访问令牌)。我不明白 1) 马洛里可以让爱丽丝点击链接,就像任何人点击链接的方式(例如,将它放在电子邮件中或在另一个网站上的误导性文字后面); 2) 交换发生在客户端的 Mallory 帐户和服务提供商之间。 我认为所描述的攻击缺少细节。 Mallory 捕获的授权 URL 并不特定于她的帐户。将其发送给 Alice 不是问题; Alice 将授予她在客户端上的帐户访问她在资源提供者上的帐户的权限。当 Mallory 将她捕获的 URL 中的 redirect_uri 更改为指向她自己的服务器(这是 OAuth2 规范允许的,但不是很常见)时,就会出现问题。在这种情况下,Mallory 的服务器将收到 Alice 的身份验证令牌。有关详细信息,请参阅hueniverse.com/…。【参考方案2】:

仅适用于 #1 -- 使用授权代码流的三足授权

当您的应用程序将授权代码交换为访问令牌时,您需要确保导致提供授权代码的 OAuth 流程实际上是由合法用户发起的。因此,在客户端应用程序通过将用户重定向到提供者来启动 OAuth 流程之前,客户端应用程序会创建一个随机状态值并将其存储在服务器端会话中。然后,当用户完成 OAuth 流程时,您检查以确保状态值与存储在用户服务器端会话中的值相匹配,因为这表明用户已经启动了 OAuth 流程。

状态值通常应该是一个不可猜测的伪随机值。可以使用 PHP 中的 rand() 函数将简单值生成为 int,但您也可以变得更复杂以提供更大的保证。

该状态的存在是为了防止像我通过电子邮件向您发送包含我帐户的授权代码的链接,您单击它并且应用程序将所有数据推送到您不知道的帐户中。

OAuth 2.0 威胁模型文档中提供了一些附加信息: https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-threatmodel-00

特别是,请参阅 CSRF 保护部分: https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-26#section-10.12

【讨论】:

您创建一个随机状态值,通常将其存储在服务器端会话中。这个“你”是谁?客户端应用程序? OAuth 授权服务器? 状态值的理想长度应该是多少?有最小长度吗?或最大长度? 客户端应用程序创建并验证它。 OAuth 授权服务器只是通过它。 重新长度最大长度将由 OAuth 提供者确定。我会使用类似 10 个字符的随机字符串。你只是想生成足够长的东西,以至于无法猜测。 让我们continue this discussion in chat【参考方案3】:

由于“状态”只是一个随机字符串,因此制作这样的东西应该可以解决问题:

$state = md5(uniqid(rand(), TRUE));

请记住将其保存在您的会话中,以便您稍后查看。

【讨论】:

-1。 php.net/manual/en/function.mt-rand.php:“注意此函数不会生成加密安全值,不应用于加密目的。如果您需要加密安全值,请考虑使用 random_int()、random_bytes() 或 openssl_random_pseudo_bytes()。”

以上是关于OAuth2.0 Server stack如何使用状态来防止CSRF?对于draft2.0 v20的主要内容,如果未能解决你的问题,请参考以下文章

oauth2-server-php-docs 概念

OAuth2.0学习(4-11)spring-oauth-server分析 - http元素使用的是何种AuthenticationManager?

轻松搭建CAS 5.x系列-在CAS Server上增加OAuth2.0协议

OAuth2.0学习(4-12)spring-oauth-server分析 - 授权码模式验证过程

OAuth2.0学习(4-15)使用token访问业务

Identity Server 4 原理和实战(完结)_Authorization Code Flow 实例