谷歌OAuth 2.0重定向URI带有多个参数

154

如何向Google OAuth 2.0的redirect_uri添加参数?

像这样:

redirect_uri=http://www.example.com/redirect.html?a=b

a=b 中的 b 是随机的。

有人可以帮忙吗?

6个回答

290
  1. 无法将任何内容添加到重定向URI中,重定向URI是OAuth应用程序设置中已设定的常量。

    例如:http://www.example.com/redirect.html

  2. 要将多个参数传递给您的重定向URI,请在调用OAuth URL之前将它们存储在state参数中, 授权后的URL将以相同的参数state=THE_STATE_PARAMETERS发送到您的重定向URI。

因此,对于您的情况,请执行以下操作:

/1. 创建您的参数的JSON字符串->

{ "a" : "b" , "c" : 1 }

/2. 进行base64Url编码,使其URL安全->

stateString = base64UrlEncode('{ "a" : "b" , "c" : 1 }');

这是一个 PHP 的 base64Url 编码和解码示例 (http://en.wikipedia.org/wiki/Base64#URL_applications):

function base64UrlEncode($inputStr)
{
    return strtr(base64_encode($inputStr), '+/=', '-_,');
}

function base64UrlDecode($inputStr)
{
    return base64_decode(strtr($inputStr, '-_,', '+/='));
}

现在状态可能是这样的:stateString -> asawerwerwfgsg,

将此状态传递到OAuth授权URL中:

https://accounts.google.com/o/oauth2/auth?
  client_id=21302922996.apps.googleusercontent.com&
  redirect_uri=https://www.example.com/back&
  scope=https://www.google.com/m8/feeds/&
  response_type=token&
  state=asdafwswdwefwsdg,

对于服务器端流程,它将随token一起发送:

http://www.example.com/redirect.html?token=sdfwerwqerqwer&state=asdafwswdwefwsdg

对于客户端流程,它将在哈希中随access token一起发送:

http://www.example.com/redirect.html#access_token=portyefghsdfgdfgsdgd&state=asdafwswdwefwsdg

检索状态,进行base64Url解码,然后进行json_decode,你就有了数据。

更多关于Google OAuth 2的信息,请参见:

http://code.google.com/apis/accounts/docs/OAuth2.html


1
Base64被用于混淆数据并进行URL编码,如果您需要通过模糊来增加一点额外的“安全性”。 - ricosrealm
21
在OAuth流程中,使用状态参数来防止CSRF攻击。在启动流程时,您必须在状态参数中设置令牌,并在重定向URI被访问时检查是否返回了相同的令牌。不要像此答案中所做的那样。可能需要考虑基于会话的解决方案。 - Rahim
2
我该如何使用 state 参数来传递多个参数到重定向 URI 并同时防止 CSRF 攻击? - hellboy
1
@hellboy 我也在想同样的问题。你是否成功将多个参数添加到状态参数中(自定义值和防止“CSRF”攻击)? - Kevin Etore
1
在 JavaScript 中,您可以在状态属性上使用 state: JSON.stringify({ a: "b", b: "c" }) - kheengz
显示剩余7条评论

18

由于被接受的答案暴露了实际数据并误用了state参数而不是坚持使用随机数来防止CSRF攻击,我将尝试展示正确的方法。不要传递(请理解为暴露)数据,应该将其保留在本地,在请求之前进行数据注水,然后在验证过的请求之后进行脱水。这里的“验证过的”意味着请求和响应的状态码必须匹配。

你需要一些临时的客户端存储。例如对于SPA或常规网站,可以将其保留在state中或使用浏览器的localStorage、会话(或带有签名的cookie)。对于移动应用程序,它们应该使用内存或任何其他本地存储。

在发送请求之前生成一个随机数(参见下文),该随机数将用作请求的state参数。将随机数与自定义状态(例如json)一起存储在本地存储中。

例如,随机数可以是ih4f984hf,自定义状态可以是{"role": "customer"}。然后,您可以像这样存储用于该请求的数据:

"ih4f984hf": {
  "role": "customer"
}

然后将仅限nonce作为请求的state参数的值。(如果您想要将nonce和数据组合成state值,请确保加密它并注意值的长度有限!)

在接收到响应时,您会获得state参数的值。查找它,如果它与本地存储中的值匹配,则可以使用存储的状态处理数据。如果nonce不匹配,则该请求可能来自攻击者,不应处理。

生成nonce

记住nonce的本质是仅限一次使用,必须是不可预测的!这里的不可预测意味着理想情况下是随机的,但实际上如果熵足够高,则准备伪随机也可以-在web应用程序中,您可能希望检查Web API Crypto,它被相当好地支持

更多阅读材料:


1
但这是可选的,而且谷歌的文档并没有让人觉得你必须实现它作为额外的安全措施。您认为每个使用Google oauth2的应用程序都应该实现这个吗? - Florian Walther
重点是,如果没有随机数,您不能依赖于重定向URL的参数。我猜可能有些情况下这并不重要,但通常情况下,您应该避免将数据放在URL中。 - Stuck
1
OAuth网站甚至声明:如果客户端希望在重定向URL中包含请求特定数据,它可以使用“state”参数来存储在用户重定向后将包含的数据。它可以将数据编码在状态参数本身中,也可以使用状态参数作为会话ID将状态存储在服务器上。来源:https://www.oauth.com/oauth2-servers/redirect-uris/redirect-uri-registration/对我来说,这听起来像是直接将重定向URL放入状态中。 - Florian Walther
1
是的,从技术上讲,您可以将数据放在那里,但从安全和隐私的角度来看,您必须考虑哪些数据可以公开,哪些需要保护以及您的应用程序应该缓解哪些攻击向量。 - Stuck
谢谢提醒! - Florian Walther
显示剩余4条评论

4

如果你正在使用.NET,你可以将参数保存在Session中。

HttpContext.Current.Session[{varname}]

并且在没有参数的情况下重定向到授权页面。
Response.Redirect(your_uri_approved_with_no_querystring_parameters);

4
当使用像Azure这样的Web农场时,这种方法无法扩展。 - spender
3
@spender: 那么你的意思是,来自同一客户端的两个几乎同时的请求可能会被Web农场中的不同服务器处理。 如果是这种情况,那么不仅仅是这样受影响,基本上在这种情况下Session变量也不能用于任何事情。 顺便说一下:我并不是在争论 - 实际上是在尝试学习这个问题。 - rufo
8
是的,完全有可能。你可以通过使用会话服务器来管理会话,或将会话备份到数据库中(参见http://msdn.microsoft.com/en-us/library/ms178586.aspx),或在负载均衡器上启用粘性会话,以确保客户端始终返回到同一Web服务器节点。我提到的所有选项都很麻烦设置,因此,在我看来,应该避免在“Session”中存储任何客户端状态。 - spender

4
在Javascript中(Node),您可以将state属性设置为键值对对象。
           const oAuth2Client = await new google.auth.OAuth2(
                clientId: <clientId>,
                clientSecret: <clientSecret>,
                redirectUrl: <redirectUrl>,
            );

            return await oAuth2Client.generateAuthUrl({
                access_type: "offline",
                scope: <scopes>,
                state: JSON.stringify({ a: "y", b: "z" }),
            });

完成谷歌认证后,它将从 URL 返回“状态”、“代码”等信息。使用以下代码将状态转换为 JSON 对象: const params = JSON.parse(state); // { a: "y", b: "z" }

2

您可以通过以下方式重定向URL参数:

当您从谷歌获得响应时,可以通过URL传递参数。

请参见以下相同的php代码:

if (isset($_GET['code'])) {
   $client->authenticate();
   $_SESSION['token'] = $client->getAccessToken();
   $redirect = 'http://' . $_SERVER['HTTP_HOST'] . $_SERVER['PHP_SELF'];
   header('Location: ' . filter_var($redirect, FILTER_SANITIZE_URL) . '?r=page/view');

在上面的例子中,r=page/view 是我想要带有参数响应的参数。

这是Google提供的PHP代码中发送状态参数的位置。服务器端进行了三个请求。这意味着最终请求将完全没有任何查询字符串变量。 - lol
非常好用!我知道我们可以在状态参数中发送信息,但是如果应用程序期望直接从请求参数中获取任何值,则会失败。您提供的方法对于此场景非常完美。谢谢! - Jayant Varshney

0
你可以使用state参数通过在base64编码的结构中使用nonce来保存数据。
如果你必须将数据传递到state参数中,请在state参数中构建带有nonce的结构。就像这样:
{
  "role": "customer",
  "nonce": "ih4f984hf",
}

一旦你对~结构~进行base64编码,你可以验证它是否被篡改。包括随机性。
警告:你需要通过你的业务逻辑验证你试图在请求中维护的数据状态是否被视为公共信息。没有任何东西可以防止恶意用户解码这些数据。
例如,如果数据仅仅代表一个颜色主题,以便用户在返回时恢复到该状态,那么无害的值是可以发送的。这取决于你的需求。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接