为什么 Spring Security 在一个请求中取消设置并设置相同的 Cookie?

信息安全 饼干 csrf 弹簧框架
2021-08-25 19:45:18

在 Spring Security 的 CSRF 实现中(https://github.com/spring-projects/spring-security/blob/master/web/src/main/java/org/springframework/security/web/csrf/CsrfAuthenticationStrategy.java# L57)他们首先“删除” XSRF-TOKEN-Cookie(第 58 行),然后直接设置一个新的(第 60 行)。

这会导致同一请求上有两个 Set-Cookie Header:

Set-Cookie: XSRF-TOKEN=; Max-Age=0; Expires=Thu, 01-Jan-1970 00:00:10 GMT; Path=/abc
Set-Cookie: XSRF-TOKEN=35ed383f-4ea1-4e00-8e5d-9bc9a3740498; Path=/abc
  • 有人可以解释为什么以这种方式完成吗?
  • 这是一种常见的模式吗?
  • 浏览器是否有任何问题,因为它们为同一个 Cookie 获取了两个标头?
2个回答

看起来这只是一个错误。

通过查看函数的历史onAuthentication,我们可以看到第一个实现只有这行:

this.csrfTokenRepository.saveToken(null, request, response);

然后,通过添加以下内容对其进行了重构:

CsrfToken newToken = this.csrfTokenRepository.generateToken(request);
this.csrfTokenRepository.saveToken(newToken, request, response);

saveToken(null, ...)尽管没有任何用途,但第一行从未被删除。

在评论中,AlphaD指出了关于 github 问题的评论,解释了为什么保留第一行:

它首先使cookie无效,然后设置cookie。如果您注销然后尝试使用令牌,这是预期的

不需要哪个,因为第二个Set-Cookie:应该覆盖以前的 cookie,从而使其无效。事实上,这种“明显”的行为并没有被指定,因此浏览器也可以随意丢弃名称冲突的 cookie 的第二个声明,这会破坏 Spring 开发人员的预期行为。

事实上,RFC6265要求不要使用Set-Cookie:相同的 cookie 名称(此处XSRF-TOKEN),因此这种行为是一种明确不鼓励的模式:

服务器不应在具有相同 cookie 名称的同一响应中包含多个 Set-Cookie 标头字段。(有关用户代理如何处理这种情况,请参阅第 5.2 节。)

由于这种模式不应该发生并且 RFC 没有提供进一步的建议,所以浏览器可以自由地按照他们的意愿解释它。如果某些浏览器更新其算法,则可能随时发生与某些浏览器的兼容性中断。

所以,由于没有人想出更好的答案,我做了一些挖掘。请注意,这不是一个权威的答案,所以对一切都持保留态度。

为什么这样做?

我认为它要么是Cargo Cult ,要么是“如果它没有坏,就不要修复它”的案例。自2013 年添加 CSRF 支持的提交以来,就存在清除 CSRF 令牌的行。

今年 3 月,GitHub 用户 AnastasiaBlack 注意到有两个 CSRF-TOKEN cookie 被保存,代码作者 rwinch 对此很有帮助地回答说她应该改为在 StackOverflow 上询问

这是一种常见的模式吗?

不会。浏览器旨在在收到具有相同名称的新 cookie 时覆盖 cookie。不需要明确的“清除”。

虽然这更像是个人轶事而不是一些官方指南,但我可以说我已经测试了我公平分享的 Web 应用程序并接收到两个具有相同名称的 cookie,一个具有空值,一个具有令牌,当然不是什么东西我在 Spring 应用程序之外遇到过。

OWASP Cheatsheet for Cross-Site Request Forgery Prevention也没有明确提及这样做,尽管他们确实建议使用 Spring Security。

当浏览器两次获取相同的 cookie 时是否会出现问题?

视情况而定™

浏览器的行为彼此不同。这个 2014 年的答案引用了RFC 6265,其中指出浏览器应该按某种顺序对 cookie 列表进行排序,并且服务器不应该依赖于发送 cookie 的顺序。

所以这意味着从技术上讲,浏览器可以随机选择其中一个,并且符合 RFC 6265,当然,除非它被编写为恶意兼容,否则没有浏览器会这样做。

由于它“似乎工作”,Spring 的维护者没有理由更改已经工作的代码。但是,如果浏览器决定对双重设置的 cookie 做出不同的反应,则可能会破坏兼容性,并且需要它们更改代码。