在使用无状态(= 无会话)身份验证时,CSRF令牌是否必要?

141

当应用程序依赖于无状态认证(例如使用HMAC)时,是否需要使用CSRF保护?

例如:

  • 我们有一个单页面应用程序(否则我们必须在每个链接上附加令牌:<a href="...?token=xyz">...</a>)。

  • 用户使用POST /auth进行身份验证。成功认证后,服务器将返回一些令牌。

  • 该令牌将通过JavaScript存储在单页面应用程序的某个变量中。

  • 此令牌将用于访问受限URL,如/admin

  • 该令牌将始终在HTTP头中传输。

  • 没有HTTP会话,也没有Cookies。

据我所知,因为浏览器不会存储令牌,所以不应该存在跨站点攻击的可能性,因此它不能自动将其发送到服务器(使用Cookies/Session时会发生这种情况)。

我错过了什么吗?


6
注意基本认证(Basic Auth)。许多浏览器会自动发送基本认证标头,直到会话结束。这会使基本认证像 cookie 认证一样容易受到 CSRF 攻击。 - phylae
2个回答

173

我找到了一些关于CSRF以及不使用cookies进行身份验证的信息:

  1. https://auth0.com/blog/2014/01/07/angularjs-authentication-with-cookies-vs-token/
    “由于您不依赖于cookies,您无需保护跨站点请求”

  2. http://angular-tips.com/blog/2014/05/json-web-tokens-introduction/
    “如果我们按照cookie的方式进行,您确实需要执行CSRF来避免跨站点请求。这是在使用JWT时可以忽略的内容。”
    (JWT = Json Web Token,一种基于令牌的认证方法适用于无状态应用程序)

  3. http://www.jamesward.com/2013/05/13/securing-single-page-apps-and-rest-services
    “不冒险使用cookies来标识用户是避免CSRF漏洞的最简单方法。”

  4. http://sitr.us/2011/08/26/cookies-are-bad-for-you.html
    “CSRF的最大问题在于,cookie绝对不能防御此类攻击。如果您使用cookie身份验证,您还必须采取其他措施来保护自己免受CSRF攻击。您可以采取的最基本预防措施是确保您的应用程序永远不会对GET请求做出任何副作用。”

有很多页面声称,如果您不使用cookie进行身份验证,则不需要任何CSRF保护。当然,您仍然可以在其他方面使用cookie,但是避免在其中存储任何类似session_id的内容。


如果您需要记住用户,有两个选项:

  1. localStorage:浏览器内置的键值存储库。存储的数据将在用户关闭浏览器窗口后仍可用。该数据对其他网站不可访问,因为每个站点都有自己的存储。

  2. sessionStorage:也是浏览器内数据存储。区别在于:当用户关闭浏览器窗口时,数据将被删除。但如果您的Web应用程序由多个页面组成,则仍然有用。因此,您可以执行以下操作:

  • 用户登录,然后您将令牌存储在sessionStorage
  • 用户单击链接,加载新页面(= 真实链接,而非JavaScript内容替换)
  • 您仍然可以从sessionStorage中访问令牌
  • 要注销,您可以手动从sessionStorage中删除令牌,或者等待用户关闭浏览器窗口,这将清除所有存储的数据。

(有关两者,请参见此处:http://www.w3schools.com/html/html5_webstorage.asp


是否有任何官方标准用于令牌认证?

JWT(Json Web Token):我认为它仍然是一份草案,但已被许多人使用,概念看起来简单而安全。(IETF:https://datatracker.ietf.org/doc/html/draft-ietf-oauth-json-web-token-25

还有很多框架的库可以使用。只需搜索即可!


39
关于CSRF的总结很好!但我需要指出,在localStorage或sessionStorage中存储令牌容易受到XSS攻击,数据可以被页面上的脚本查看 - 如果你从CDN提供了受损脚本,或者JS库中有恶意代码,它们可以窃取这些存储位置中的令牌。请参见:https://stormpath.com/blog/where-to-store-your-jwts-cookies-vs-html5-web-storage/ 我认为最安全的方法是将JWT + CSRF令牌存储在cookie中,然后将包含CSRF令牌的计算JWT放置在请求头中。 - Aaron Gray
关于:“您可以采取的最基本预防措施是确保您的应用程序永远不会对GET请求做出任何副作用反应。”CSRF攻击是否可能伪造POST请求? - Costa Michailidis
根据服务器端应用程序的不同,这是有可能的。有些Web框架使用类似于http://.../someRestResource?method=POST的东西。因此,它基本上是一个GET请求,但是服务器应用程序将其解释为POST请求,因为它被配置为使用method参数而不是HTTP头。关于常见的Web浏览器,它们强制执行同源策略,并且只会对外部服务器执行GET请求。虽然如果Web浏览器不遵循这些Web标准(存在漏洞、恶意软件),则可能执行POST请求。 - Benjamin M
1
Server Side App的补充:仍然无法发送请求正文,因为通用浏览器不允许这样做。但是,如果服务器应用程序允许 method=POST,它也可能允许 body={someJson} 来覆盖默认请求正文。这是非常糟糕的API设计,极其危险。虽然如果您的服务器应用程序允许 http://...?method=POST&body={someJson},您应该真正重新考虑您所做的事情以及为什么以及是否有必要。(我会说在99.9999%的情况下这是必要的)。此外,浏览器只能通过这种方式发送几千字节。 - Benjamin M
@BenjaminM 注意,同源策略只是防止 JavaScript 代码访问结果,因此虽然请求被“阻止”,但实际上已经到达服务器了-http://jsbin.com/mewaxikuqo/edit?html,js,output 我只在 Firefox 上测试过,但您可以打开开发工具并查看即使您收到“跨源请求已阻止”,远程服务器实际上也能看到整个请求。这就是为什么您必须为所有 POST 请求提供令牌或自定义标头(如果可能,则两者都提供)。 - Yoni Jah
显示剩余2条评论

67

TL;DR:

如果没有使用Cookies,JWT可以替代需要CSRF令牌的功能。但是,如果在session/localStorage中存储JWT,则会暴露您的JWT和用户身份,如果您的站点存在XSS漏洞(相当普遍)。更好的做法是在JWT中添加csrfToken键,并将JWT存储在具有secure和http-only属性设置的Cookie中。

阅读此文章以获取更多信息: https://stormpath.com/blog/where-to-store-your-jwts-cookies-vs-html5-web-storage

您可以通过包括xsrfToken JWT声明使此CSRF保护无状态:

{ "iss": "http://galaxies.com", "exp": 1300819380, "scopes": ["explorer", "solar-harvester", "seller"], "sub": "tom@andromeda.com", "xsrfToken": "d9b9714c-7ac0-42e0-8696-2dae95dbc33e" }

因此,您需要将csrfToken存储在localStorage/sessionStorage中,并在JWT本身中存储它(该JWT存储在一个具有http-only和secure属性设置的cookie中)。然后,为了进行csrf保护,请验证JWT中的csrf token是否与提交的csrf-token头匹配。


2
在用户API身份验证期间,是否应该豁免CSRF令牌的使用? - user805981
4
值得指出的是(正如其他评论中在源链接上也提到的那样),任何使用a)非HttpOnly的cookie,或b)将CSRF令牌存储在本地存储中的CSRF缓解都容易受到XSS攻击。这意味着所提出的方法可能有助于防止使用XSS的攻击者获得JWT密钥,但攻击者仍然可以执行恶意请求到您的API,因为他能够提供有效的JWT(通过cookie,感谢浏览器)和CSRF令牌(通过注入的JS从本地存储/cookie中读取)。 - Johannes Rudolph
2
实际上,在这种级别的XSS中,即使有CSRF令牌也无法保护您,因为您假设攻击者可以访问localStorage,目前唯一访问它的方式是获得脚本级别的访问权限,他们仍然可以查看CSRF令牌。 - itsnotvalid
2
不是完全的专家,但如果您仍然像开始时一样面临XSS攻击,我不确定*最好添加...*这部分是否真的适用。攻击者可能更难获取CSRF令牌,但最终他仍然能够代表您执行请求,即使实际上并不知道JWT令牌。这正确吗?谢谢。 - superjos
1
JWT 无法从 http-only Cookie 中窃取,但 CSRF 令牌可能会被窃取,因为需要在可读的位置使用。如果页面上存在不受信任的 JavaScript,则仍然容易受到攻击,但我不知道除了小心避免之外,是否有任何真正的保护措施来防止不受信任的 JS。如果您未使用 cookie,则将暴露认证令牌,如果您使用 cookie,则将暴露 CSRF 令牌。我的理解正确吗? - Samo
显示剩余4条评论

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