在Node.js中创建和存储OAuth请求的CSRF令牌的最佳方法是什么?

huangapple go评论109阅读模式
英文:

Best method for creating and storing csrf token for oauth requests in nodejs

问题

最近,我被分配了创建自定义OAuth登录和注册流程的任务。所以我很好奇,生成和存储OAuth请求的CSRF令牌的最佳方法是什么。我已经阅读了几篇关于这个主题的文章,比如来自Auth0的这篇文章。它们指出,应该将CSRF令牌(随机字符串)放在OAuth提供程序请求的状态中,然后在本地存储。然后,当回调URL被访问时,您可以检查状态中的令牌与本地存储的令牌是否匹配,以确定请求是否有效。

然后,我阅读了来自Bright Sec的以下文章。它提到:

就像一般的会话令牌一样,CSRF令牌应该包含足够的熵并且强烈不可预测。

您可以通过使用加密强度的伪随机数生成器(PRNG),以创建时的时间戳和一个静态密钥作为种子,来实现这一点。

所以这意味着它们应该不仅仅是一个随机字符串,就像Auth0文章中所说的那样。因此,像UUID这样的东西(这是我的最初想法)可能不太符合良好CSRF令牌的标准。

此外,我遇到的大多数文章都是关于提交表单的。

所以我的问题是,在Node.js中创建CSRF令牌的最佳方法是什么,以及在进行像Google和Facebook这样的提供商的身份验证时,应该在本地存储在哪里?

我目前的理解是应该发生以下情况。

  1. 用户启动身份验证请求,在这一点上,您创建一个CSRF令牌(仍然不确定最佳方法是什么),并将其保存在本地(也许是会话?)。
  2. 将此令牌发送到OAuth提供程序的状态参数中。
  3. 当OAuth提供程序回应您的回调URL时,它将在状态参数中将该令牌发送回给您,您应该将这两个令牌进行比较,以确保请求是真实的。

有一些用于Node.js的CSRF库,主要是Express中间件,但我不太确定它是否适用于OAuth请求。我一直在努力查阅Passport.js和Next-Auth,看看它们的方法是什么,但我认为这让我更加困惑。从下面的图像中,似乎Next Auth将CSRF令牌存储为浏览器中的cookie:

在Node.js中创建和存储OAuth请求的CSRF令牌的最佳方法是什么?

因此,我只能假设CSRF令牌基本上只是为了确保如果有人拦截了从OAuth提供程序返回到源回调URL的请求,他们不能从其浏览器中使用它。所以CSRF令牌只是为了确保命中回调URL的请求是从启动它的同一个浏览器发起的。对吗?

无论如何,对于如何创建令牌和在哪里存储它,我都会感激任何帮助。

英文:

I have recently been tasked with creating a custom oauth login and signup flow. So I was curious what is the best way to go about generating and storing csrf tokens for oauth requests. I have read several articles on this like this one from Auth0. They state that a csrf token (random string) should be placed in the of the request to the oauth provider then stored locally. Then when the callback url is hit you can check the token in the state with the one you stored locally to see whether the request is valid.

So then I read the following article from Bright Sec. It states that

> Just like session tokens in general, CSRF tokens should contain significant entropy and be strongly unpredictable.
>
> You can achieve this by using a cryptographic strength pseudo-random number generator (PRNG), seeded with the timestamp when it was created and a static secret.

So this says that they should be a little more than just a random string like the auth0 article. So something like a uuid (which was my initial thought) may not really meet the standards for a good csrf token.

Also most of these articles that I have come across have been for submitting forms.

So my question is what is the best way to create a csrf token in nodejs and where should I store it locally when authenticating to providers like Google and Facebook?

My current understanding of what should happen.

  1. User starts an authentication request and at this point you create a csrf token (still not sure best method for doing this) and save it locally (Maybe to a session?).
  2. Send this token to the oauth provider in the state param
  3. When the oauth provider responds back to your callback url they will send that token back to you in the state param and you should compare the 2 to make sure the request is authentic.

There are crsf libraries for nodejs. Mostly Express middleware but I'm not really sure that would apply to oauth requests. I have been trying to comb through passportjs and next-auth to see what their methods are but I think it is leaving me more confused than anything. It seems like Next Auth stores the csrf token as a cookie in the browser as shown in the image below:

在Node.js中创建和存储OAuth请求的CSRF令牌的最佳方法是什么?

So I can only assume from this the the csrf token is basically just to make sure that if somebody intercepts the request from the oauth provider back to the origins callback url they cannot use it from their browser. So the csrf token is just to make sure that the the request hitting the callback url was initiated from the same browser that it started. Correct?

Anyway any help here would be appreciated on how to create the token and where should I store it?

答案1

得分: 1

我认为了解可能发生的攻击方式对于理解CSRF如何有助于防止攻击是有益的。

用户开始认证请求,此时您创建一个CSRF令牌(仍然不确定最佳方法),并将其保存在本地(可能是在会话中?)。
将此令牌发送到OAuth提供程序的状态参数中。
当OAuth提供程序响应返回到您的回调URL时,它们将在状态参数中将该令牌发送回给您,您应该将这两个令牌进行比较,以确保请求是真实的。

是的,您在这里是正确的。如果没有CSRF,恶意用户可以捕获URL,并让第三方访问该URL,他们将能够访问其帐户。

您可以通过创建一个随机化的状态值来防止这种情况,使用恶意用户无法猜测的内容,例如与userId关联的会话密钥。

没有标准化的存储此密钥的方法。您还可以使用基于TOTP的解决方案,如果OAuth2流程返回的时间太长,您可以使用它来使令牌失效。这允许您只需将用于生成密钥的临时密钥存储在与某个用户标识符相关联的服务器端的Redis缓存中。这样,在验证步骤中,您只需要基于存储的临时密钥重新创建CSRF,并进行验证。

现在,您可以像您提到的那样启动常规的OAuth2流程,然后比较令牌以进行验证。

希望这有所帮助,其他专家可能会发表意见。我正在手机上撰写,所以请原谅任何格式问题。

英文:

I think its beneficial to understand how a possible attack can happen in order to understand how CSRF can potentially help.

> User starts an authentication request and at this point you create a csrf token (still not sure best method for doing this) and save it locally (Maybe to a session?).
Send this token to the oauth provider in the state param
When the oauth provider responds back to your callback url they will send that token back to you in the state param and you should compare the 2 to make sure the request is authentic.

Yes, you are correct here. If there was no CSRF here, a malicious actor could just capture the URL and get a third party to visit that url instead, they will be able to access their account.

You can prevent this by creating a randomised state value using something that is unguessable by the malicious actor eg a session key tagged to userId.

There is no standardised way of storing this key. You could also a use TOTP based solution which you can use to invalidate token if it takes too long for the oauth2 flow to return. This allows you to just store the temporary secret used to generate the key tagged to some user identifier which you can just store serverside in a redis cache. This way during the validation step you just need to recreate the CSRF based on the stored temp key and validate.

So now you initiate your normal oauth2 flow as you mentioned and then compare the token for validation.

I hope this helps and other experts could chime in. I am writing this on mobile so pardon any formatting issues.

huangapple
  • 本文由 发表于 2023年7月14日 04:59:35
  • 转载请务必保留本文链接:https://go.coder-hub.com/76683210.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定