简体   繁体   English

在Python REST API中实现CSRF保护

[英]Implementing CSRF protection in a Python REST API

Writing a REST API with Pyramid/Cornice using JWT for authentication, I'll have to implement some CSRF protection. 使用JWT编写带有Pyramid / Cornice的REST API进行身份验证,我将不得不实施一些CSRF保护。 Having thoroughly read up on the topic I understand the problem, but I'm pretty confused about the best way to implement it, it's a bit tricky considering all the possible attack vectors. 彻底阅读了这个主题我理解了这个问题,但我对实现它的最佳方法感到困惑,考虑到所有可能的攻击向量,这有点棘手。

Since this API gives access to sensitive data and will be published as open source software, it requires a self-contained protection. 由于此API可以访问敏感数据并将作为开源软件发布,因此需要一个独立的保护。 It will be used in environments with untrusted subdomains and I can not rely on users to follow security guidelines. 它将用于具有不受信任的子域的环境中,我不能依赖用户遵循安全准则。

For my stateless service I can either use " Double Submit Cookies " or the " Encrypted Token Pattern "-method. 对于我的无状态服务,我可以使用“ Double Submit Cookies ”或“ Encrypted Token Pattern ”方法。

Double Submit Cookies 双提交Cookies

To prevent "cookie tossing" , the token in the Double Submit method needs to be verifiable. 为了防止“cookie抛出” ,Double Submit方法中的令牌需要是可验证的。 MiTM attacks are an additional threat, which I hope to mitigate sufficiently by forcing HTTPS-cookies only. MiTM攻击是一种额外的威胁,我希望通过仅强制使用HTTPS-cookies来充分缓解这种威胁。

To get a verifiable token that can't be easily guessed and replicated by an attacker, I imagine a hashed token like this should work: 为了获得一个不易被攻击者猜测和复制的可验证令牌,我想像这样的散列令牌应该有效:

pbkdf2_sha256.encrypt($userid + $expire + $mycsrfsecret, salt=$salt)

"exp" is the expire-value from the JWT. “exp”是JWT的到期值。 The JWT will be issued together with the CSRF-cookie and "exp" can be read by the server, which adds some additional protection as it's variable and the attacker doesn't know it (Might be superfluous?). JWT将与CSRF-cookie一起发布,服务器可以读取“exp”,这会增加一些额外的保护,因为它的变量和攻击者不知道它(可能是多余的?)。

On a request I can easily compare the two tokens I receive with each other and use pbkdf2_sha256.verify($tokenfromrequest, $userid + $exp + $mycsrfsecret) to compare it with the values from the JWT-token ('Verifiablity'). 根据请求,我可以轻松地比较我收到的两个令牌,并使用pbkdf2_sha256.verify($tokenfromrequest, $userid + $exp + $mycsrfsecret)将其与JWT-token('Verifiablity')中的值进行比较。

Would that approach follow recommended practices? 这种方法会遵循建议的做法吗?

I've selected pbkdf2 over bcrypt since its verify-method is noticeably quicker. 我选择了pbkdf2 over bcrypt,因为它的验证方法明显更快。

Expiry would be set to 7 days, after that both the JWT and the CSRF-token would be renewed by a fresh login (They would also be renewed on an intermediate relogin). 到期时间设置为7天,之后JWT和CSRF令牌将通过新登录续订(它们也将在中间重新登录时续订)。

Encrypted Token Pattern 加密令牌模式

The alternative is to send a string to the client, consisting of userid, expiry and nonce, encrypted with a server-side secret. 另一种方法是向客户端发送一个字符串,包括userid,expiry和nonce,用服务器端密码加密。 On a request this string is sent along and the server can decrypt it and verify userid and expiry. 在请求中,此字符串将一起发送,服务器可以对其进行解密并验证用户ID和到期日期。

This seems the simpler approach, but I'm unsure how to implement it, I don't intend to roll my own crypto and I have not found good examples: 这似乎是更简单的方法,但我不确定如何实现它,我不打算滚动我自己的加密,我没有找到好的例子:

  • What cipher/library should I use in Python? 我应该在Python中使用什么密码/库? How do I do Encrypt-then-MAC? 我怎么做Encrypt-then-MAC?
  • How would I persist the token until its natural expiration? 如何将令牌持续到其自然到期之前? I don't want the users to have to login freshly every time they restart their browsers. 我不希望用户每次重新启动浏览器时都必须重新登录。 Local Storage is not a safe place - but there is no alternative. 本地存储不是一个安全的地方 - 但没有其他选择。

Writing a REST API with Pyramid/Cornice using JWT for authentication 使用JWT编写带有Pyramid / Cornice的REST API进行身份验证

While I am not familiar with those frameworks, I suggest you ensure the JWT token is passed within a HTTP header (eg My-JWT-Token: ... ) which is NOT the cookie. 虽然我不熟悉这些框架,但我建议您确保JWT令牌在HTTP头(例如My-JWT-Token: ... )中传递,而不是cookie。 Then you do not have to worry about the CSRF vector. 然后您不必担心CSRF向量。

Cross Site Request Forgery is an issue due to the nature of the browser's tendency to always submit cookies, which often contain authentication information, to a particular domain. 跨站点请求伪造是一个问题,因为浏览器倾向于始终向特定域提交cookie(通常包含身份验证信息)。 A browser will not automatically submit a custom header, ergo you do not have to worry. 浏览器不会自动提交自定义标题,您不必担心。

Double Submit Cookies 双提交Cookies

Your method is overly complicated, you could simply use a GUID. 您的方法过于复杂,您只需使用GUID即可。 Put that GUID in a cookie, and put it in any other part of the request. 将该GUID放入cookie中,并将其放入请求的任何其他部分。 If they equal, CSRF check passed. 如果他们相等,CSRF检查通过。 You could also put the GUID into the JWT, then validate the GUID is also in a header/body/query parameter. 您还可以将GUID放入JWT,然后验证GUID也在标头/正文/查询参数中。

Encrypted Token Pattern 加密令牌模式

This is almost exactly what JWT is, just pass the token in the header as suggested 😄 这几乎就是JWT,只是按照建议pass传递标题中的标记

To answer the questions: 回答问题:

  • I would suggest hmac as in import hmac . 我建议hmac作为import hmac I would not bother encrypting but merely ensure there is no sensitive information in the token. 我不打扰加密,只是确保令牌中没有敏感信息。 Else PyCrypto may do you well. 其他PyCrypto也可以帮到你。
  • This is why cookies exist, which does raise the CSRF issue again. 这就是为什么存在cookie,这确实再次引发了CSRF问题。 If this is a hard requirement then I suggest the double submit cookie method. 如果这是一个硬性要求,那么我建议使用双提交cookie方法。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM