CSRF令牌为什么需要加密?

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

Why are CSRF tokens encrypted?

问题

我正在努力将安全CSRF令牌实现到我的Node.js后端和React前端应用中,该应用使用express-sessions。我已经创建了这个模块来生成、验证和存储Redis中的CSRF令牌,并包括一些额外的安全性,如每个令牌的独立密钥以防止BREACH攻击,针对每个功能的令牌,以及支持同一功能的多个选项卡(因此令牌ID和密钥)。

我了解到CSRF令牌是加密的,只有服务器知道密钥,所以当浏览器将令牌发送到服务器时,服务器可以通过秘密密钥验证,确保只有服务器知道。 我的问题是为什么要加密

据我理解,如果攻击者以某种方式窃取了加密的令牌,那么加密就没有用了,因为当他们提交带有令牌的请求时,服务器会验证它,因为它本质上是相同的加密令牌。如果是这样的话,将令牌存储在服务器上并检查客户端提交的令牌是否匹配是否会更高效?(当然要考虑比较中的时序攻击)

谢谢

英文:

I'm working on implementing secure CSRF tokens into my nodejs backend & react frontend app that uses express-sessions. I have created this module to generate, validate, and store CSRF tokens in redis & include some extra security like separate secret per token for BREACH security, per-feature tokens, & support for multiple tabs of the same feature (thus the token IDs and keys).

I have read that CSRF tokens are encrypted and only the server knows the key, so that when the browser sends the token to the server the server can validate via the secret key that only the server knows. My question is why are they encrypted?

To my understanding, if an attacker somehow manages to steal the encrypted token then the encryption is useless, as when they submit a request w/ the token the server would validate it since it is essentially the same encrypted token. If that is the case, wouldn't it be more performant to just store the token on the server as well and check if the one the client submits matches? (Of course accounting for timing attacks in the comparison)

Thank you

答案1

得分: 2

CSRF令牌通常不会被加密。在一个典型的实现中,比如在普通的Web应用中使用同步器令牌模式,CSRF令牌只是一个足够大的随机值,存储在服务器上并在生成表单时也提供给客户端。客户端可以随后将其与表单一起发送回来,证明发送的表单确实是由服务器生成的,而不是其他人。(即使在其他模式(如双重提交)的情况下,作为标题字段和Cookie发送的令牌在基本情况下也只是一个随机令牌。)

然而,有两点需要注意。

同步器令牌模式(经典的CSRF令牌)需要进行服务器查找。如果已经有用户会话,这并不是一个大问题,但并非始终如此,有些应用程序设计为无状态。在这种情况下,您不能只使用随机令牌,因为在不检查服务器端状态的情况下,您无法确定它是否有效。

另一点需要注意的是,如果令牌包含有关客户端的一些信息,实际上可以进一步增加安全性。例如,如果令牌与当前客户端的IP地址相关联(在较为简单的实现中),那么某种程度上就不那么容易窃取CSRF令牌了。同样,您可以将此附加信息存储在服务器端,但这又涉及到状态,有些应用程序希望避免以简化负载平衡等操作。

因此,关键在于无状态的CSRF令牌,即无需进行后端(数据库)查找就能直接检查的令牌。

您可以做的(以及一些框架为您做的)是创建一个包含一些嵌入数据的结构化令牌,并使用服务器唯一知道的密钥对其进行加密。然后,服务器将其作为CSRF令牌发送,并期望在更改状态的请求上收到它。当服务器收到它时,不需要进行数据库查找,它只需解密令牌并查看是否是服务器创建的有效令牌。

请注意,仅出于此目的,您实际上不需要加密,更适合的加密原语将是消息认证码,因为您只关心令牌的真实性,即服务器自己是否创建了它,而不是其他人。但是,某些框架在令牌中包含的数据往往受到加密的进一步保护(以及通过适当的带身份验证的加密算法的隐式消息认证)。但在非常基本的实现中,您实际上可以只包括时间戳和用户ID,并将其作为无状态的CSRF令牌进行HMAC签名(但包括更多信息,甚至是有关生成的表单字段的信息,可能会进一步增加安全性)。

总之,未加密的随机令牌被认为对于CSRF足够,而在双重提交的情况下,它们也可以是无状态的(由于同源策略,攻击者无法将相同的随机令牌同时作为Cookie和标题发送到不同的源)。但如果需要更高的安全性,加密的、包含更多信息的令牌可以提供更多安全性,甚至在一定程度上减轻被窃取的CSRF令牌的威胁。

英文:

CSRF tokens are not normally encrypted. In a textbook implementation using for instace the synchronizer token pattern in a normal web app, a CSRF token is just a sufficiently large random value, stored on the server and also given to the client upon form generation. The client can then send it back with the form, proving that the form it is sending was actually generated by the server, and not somebody else. (Even in case of other patterns like double posting, the token sent as something like a header field and a cookie is just a random token in the base case.)

However, there are two things to note.

The synchornizer token pattern (a classic CSRF token) needs a server lookup. This is not so big of a deal if there is a user session anyway, but that's not always the case, some applications are designed to be stateless. In that case you can't just have a random token, because you can't decide without checking server-side state whether it's valid.

The other thing to note is that you can actually increase security even more, if the token contains some information about the client. Like for example it's less useful to somehow steal a csrf token, if it's somewhow tied to the client anyway (for example if it is associated with the current client IP address in a more naive implementation). Again, you could store this additional information server-side, but that's again state, that some applications want to avoid to make things like load balancing easier.

So it comes down to stateless CSRF tokens, ones that you can just check as they are, without state (database) lookups on the backend.

What you can do (and what some frameworks do for you) is they create a structured token with some data embedded in it, and they encrypt it with a key only known to the server. The server then sends this as the CSRF token, and expects to receive it back upon state changing requests. When it receives it back, the server doesn't need a database lookup, it can just decrypt the token and see if it's a valid one that the server created.

Note that purely for this purpose, you don't actually need encryption, the crypto primitive more suitable would be a message authentication code, because you only care about the authenticity of the token, ie. that the server itself created it and not somebody else. However, the data some frameworks include in the token is many times further protected by encyption (and implicit message authentication by a suitable authenticated encryption algorithm). But in a very basic implementation, you could actually just include a timestamp and user id with hmac as a stateless CSRF token (but including more information, maybe even about the form fields generated would further increase security).

So in short, unencrypted random tokens are considered sufficient for CSRF, and in case of double posting, they can also be stateless (because of the same origin policy, an attacker cannot post the same random token to a different origin both as a cookie and as a header). But an encrypted, more information rich token can provide more security if that's needed, potentially even somewhat mitigating the stolen CSRF token threat too.

huangapple
  • 本文由 发表于 2023年1月6日 21:23:15
  • 转载请务必保留本文链接:https://go.coder-hub.com/75031513.html
匿名

发表评论

匿名网友

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

确定