【问题标题】:Browser serving an obsolete Authorization header from cache浏览器从缓存中提供过时的授权标头
【发布时间】:2017-11-24 00:31:46
【问题描述】:

我的客户端在向我的服务器发出无害请求后被注销。我控制了两端,经过大量调试,我发现发生了以下情况:

  • 客户端发送带有正确授权标头的请求。
  • 服务器以304 Not Modified 响应,没有任何授权标头。
  • 浏览器提供完整的响应,包括在其缓存中找到的一个过时的授权标头
  • 从现在开始,客户端使用过时的授权并被踢出。

据我所知,the browser must not cache any request containing Authorization。尽管如此,

chrome://view-http-cache/http://localhost:10080/api/SearchHost

表演

HTTP/1.1 200 OK
Date: Thu, 23 Nov 2017 23:50:16 GMT
Vary: origin, accept-encoding, authorization, x-role
Cache-Control: must-revalidate
Server: 171123_073418-d8d7cb0 =
x-delay-seconds: 3
Authorization: Wl6pPirDLQqWqYv
Expires: Thu, 01 Jan 1970 00:00:00 GMT
ETag: "zUxy1pv3CQ3IYTFlBg3Z3vYovg3zSw2L"
Content-Encoding: gzip
Content-Type: application/json;charset=utf-8
Content-Length: 255

有趣的服务器标头用一些内部信息替换了 Jetty 服务器标头(出于安全原因不应提供) - 忽略它。这就是 curl 所说的:

< HTTP/1.1 304 Not Modified
< Date: Thu, 23 Nov 2017 23:58:18 GMT
< Vary: origin, accept-encoding, authorization, x-role
< Cache-Control: must-revalidate
< Server: 171123_073418-d8d7cb0 =
< ETag: "zUxy1pv3CQ3IYTFlBg3Z3vYovg3zSw2L"
< x-delay-seconds: 3
< Content-Encoding: gzip

这也发生在 Firefox 中,尽管我目前无法重现它。 The RFC 继续,看起来上面链接的答案并不准确:

除非响应中存在允许存储此类响应的缓存指令

看起来响应是可缓存的。没关系,我确实希望 内容被缓存,但我不希望从缓存中提供 Authorization 标头。这可能吗?

解释我的问题

我的服务器过去只在响应登录请求时发送Authorization 标头。这曾经可以正常工作,但新的要求会带来问题。

我们的网站允许用户任意长时间保持登录状态(我们不从事敏感业务)。我们正在更改授权令牌的格式,我们不想因此而强制所有用户重新登录。因此,我让服务器在看到过时但有效的授权令牌时发送更新的授权令牌。所以现在任何响应都可能包含授权令牌,但大多数都没有。

浏览器缓存将仍然有效的响应与过时的授权令牌相结合。

作为一种解决方法,当存在授权令牌时,我让服务器不发送etag。它有效,但我更喜欢更清洁的解决方案。

【问题讨论】:

  • 如果链接的答案让你相信浏览器不能缓存任何包含授权的东西,那么它就是误导。实际上恰恰相反,这意味着 Chrome 的缓存按部就班地完成了它的工作。我怀疑你已经想通了。您只需要更多信息来设计解决方案,对吧?
  • @Rei 对,我对链接的答案感到困惑。我也对 RFC 感到困惑,因为这种行为对我来说毫无意义。有关问题,请参阅问题更新。

标签: caching browser http-headers browser-cache


【解决方案1】:

链接答案中的引用具有误导性,因为它省略了一个重要部分:“如果缓存是共享的”。 这是正确的报价 (RFC7234 Section 3):

缓存不得存储对任何请求的响应,除非: ... Authorization 标头字段(参见 [RFC7235] 的第 4.2 节)没有出现在请求中,如果缓存是共享的,

RFC 的那部分基本上是一个摘要。 这是完整的规则 (RFC7234 Section 3.2),其内容基本相同:

共享缓存不得对带有授权头字段([RFC7235] 第 4.2 节)的请求使用缓存响应来满足任何后续请求,除非缓存响应中存在允许存储此类响应的指令。

浏览器缓存是共享缓存吗? 这在Introduction section of the RFC中有解释:

相比之下,私有缓存专用于单个用户;通常,它们被部署为用户代理的组件

这意味着浏览器缓存是私有缓存。 它不是共享缓存,因此上述规则不适用,这意味着 Chrome 和 Firefox 都可以正常工作。

现在是解决方案。

规范建议在没有 Authorization 标头的情况下重用包含 Authorization 的缓存响应的可能性。 不幸的是,它还说该功能并未广泛实施。

所以,我能想到的最简单也是最面向未来的解决方案是确保不缓存任何包含授权令牌的响应。 例如,每当服务器看到一个过时但有效的授权令牌时,发送一个新的有效令牌以及 Cache-Control: no-store 以禁止缓存。

此外,您绝不能发送带有 Authorization 标头的 Cache-Control: must-revalidate,因为 must-revalidate 指令实际上允许缓存响应,包括共享缓存,这可能会在未来导致更多问题。

...除非响应中存在允许存储此类响应的缓存指令。

在本规范中,以下 Cache-Control 响应指令(第 5.2.2 节)具有这样的效果:must-revalidate、public 和 s-maxage。

【讨论】:

  • 你的解释很完美,但我不太喜欢这个解决方案。如果没有must-revalidate,浏览器会感觉它自己可以决定响应是否有效,这是错误的。我都不喜欢对包含授权标头的请求禁用缓存,因为内容应该被缓存;只是过时的标题不应该。但我担心 HTTP 不支持我想要的,所以我不得不接受你的回答。
  • @maaartinus 我理解你的失望。再说一次,该规范于 2014 年发布,情况可能会有所改善。你可能想试试Cache-Control: no-cache=authorization
【解决方案2】:

我目前的解决方案是在每个响应中发送一个授权标头;在不需要授权时使用占位符值-

占位符值显然是没有意义的,客户端知道它并愉快地忽略它。

这个解决方案很丑陋,因为它可能会为每个响应添加 20 个字节,但这仍然比像我的问题中提到的方法那样偶尔不得不重新发送整个响应内容要好。此外,使用 HTTP/2,它将是免费的。

【讨论】:

    猜你喜欢
    • 2020-06-08
    • 2018-02-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-04-23
    • 2019-07-03
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多