仅使用 SameSite Lax cookie 的 SSO 的可行性?

Feasibility of SSO with SameSite Lax cookies, only?

背景

我今天正在考虑为我的 cookie 实施 SameSite 的方面。我已经有 HttpOnlySecure,所以我认为这可能没什么大不了的。

为什么坏了

好吧,事实证明,一旦我实施了设置,很多东西都坏了。 SameSite=LaxSameSite=Strict 都发生了这种情况。我做了一些研究,发现这是由于 SSO 容易在 SameSite 设置为 LaxStrict(而不是 None)时损坏:

我的主要浏览器 (Iron 70) 基于 Chromium 70,所以我以前从未遇到过在 2 月份向 Chrome 80 名用户推出的更改,据说默认的 cookie 没有 SameSite 值至 Lax。我安装了最新的 Google Chrome Portable 来检查它,有趣的是,该功能目前似乎 而不是 (谢天谢地)默认为 SameSite=Lax 因为它可能曾经 - 我的网站只有在我明确启用以下 header:

后才会出现故障

Header edit Set-Cookie ^(.*)$ ;SameSite=Lax

这似乎是因为没有明确的 SameSite,Chromium 默认将其视为 "LAX + POST w/ 2-minute rule"(我正在快速测试,因此在 2 分钟内)。

即使使用 Lax,我的所有 single-sign 都坏了,我的实时聊天也不再工作了——无论是使用 Websockets 还是 XHR 请求。当我尝试打开 single-sign 时,不知何故我最终退出了主网站,这也没有多大意义 - 基本上,一切都搞砸了。

  1. 是否有希望让 XHR 或 Websockets 再次与 Lax 一起工作?我在 chat.example.com 聊天,但我也允许在 sub.someotherdomain.org 的侧面板中访问它。我的猜测是这里的答案是 no,绕过它的唯一方法是使 URL 在同一域上可用,而 Apache 只是指向场景。很烦人,但是 可以 完成 - 但还有其他方法吗?

  2. 我更大的问题是:single-sign 是否与 LaxStrict 本质上不兼容?我并没有真正找到太多的方法。所有的文章似乎都把用 Lax 破坏 SSO 视为不可避免,甚至有一些图表解释了 为什么 它会破坏,但是 SSO 必须那样吗?

主流解决方法

大多数网站都说要 SameSite=None 来解决这个问题并在所有用户代理中强制执行旧行为。从技术上讲,这是可行的,但我想知道是否有任何希望能够使用 Lax 来代替?如何才能在不屈服于 SameSite=None 的情况下发挥作用?

TL;DR - Yes, you can use SameSite=Lax (but not SameSite=Strict) and not break SSO!

关于SameSite cookie有两点需要注意:

  • Lax 禁止 cross-site 使用 POST
  • 的请求
  • 严格 也禁止 cross-site 使用 GET
  • 的请求

有用的总结:

来源:https://www.wst.space/cookies-samesite-secure-httponly/

Strict 根本行不通,因为它会阻止任何类型的 cross-site 请求发送 cookie,这使得 SSO 完全不可能。 Strict 甚至不是一个可行的候选人。

剩下 LaxNone(到目前为止一直是默认值,并且正在慢慢被 Lax 取代)。

  1. Is there any hope of getting XHR or Websockets to work again with Lax? I have chat up at chat.example.com, but I also allow access to it in a side panel on sub.someotherdomain.org. My guess is the answer here is no, and the only way to get around it would make a URL available on the same domain which Apache simply points to the same script behind the scenes. Annoying, but it could be done - but is there another way?

没有.

此处最好的解决方案是在幕后重写 URL,这样您就不需要维护重复的资源。使用 Apache 的 mod_rewrite 重写 URL 或简单地执行 include('path/to/file.php') 将是一个简单的解决方案。返回的 content 将完全相同 - 但如果它需要发送 Lax cookie,则浏览器必须将它们发送到作为当前域。

  1. My bigger question is: is single-sign on inherently incompatible with Lax and Strict?

没有,还好没有!

I've not really found much in the way of this. All the articles seem to treat breaking SSO with Lax as inevitable, and there are even some diagrams that explain why it breaks, but does SSO have to be that way?

是的,很多SSO页面确实SameSite=Lax - 但这种失败 不是 不可避免 - 它是 implementation-specific。让我们将原始方法与兼容 SameSite=Lax cookies 的修改方法进行比较。

原始 SSO 过程(需要 SameSite=None

  1. 用户导航至 sub.example.org - 当前未登录,因为未在此站点上设置 cookie
  2. 页面检测到未登录并自动重定向到 example.com 上的 SSO 页面 - 如果用户未在那里进行身份验证,它会重定向回来并给出 username/password 提示。如果用户 已通过身份验证,则继续。
  3. example.com 上,读取用户的会话数据并为 SSO 调用创建唯一令牌。将令牌转储到数据库中,然后 POST 使用插入的令牌返回原始站点。
  4. 返回 sub.example.org,读取 POSTED 的令牌并在数据库中查询该令牌,然后从中检索用户 ID。
  5. sub.example.org 的本地会话中设置用户 ID - 现在会话按预期工作,因为 $_SESSION['mysession'] returns example.comsub.example.org(因为用户 ID 永远不会改变,从技术上讲,这些是重复的 cookie)。

这将打破 SameSite=Lax。为什么?因为对身份验证器的原始请求使用的是 POST 请求 - 这是针对外国域的 - SameSite=Lax SameSite=Strict - 和 cross-domain POSTs 不会将 cookie 发送到目的地。因此,cookie 不可用并且身份验证器不知道哪个用户已通过身份验证,因此它不能 在回发之前为该用户创建临时令牌。这就是为什么这不起作用。

但是,这里要注意的重要一点是 POST 请求 没有发送任何敏感数据 (至少在上述实施中)。它只是 要求 进行身份验证 - 它甚至没有任何敏感数据要发送!

那么,为什么我们首先 POSTing?回想一下 SameSite=Lax 允许 first-level GET 导航(SameSite=Strict 不允许)。因此,我们可以通过简单地使用 GET 而不是 POST 来利用这一点作为 initial 重定向 only.

解决方法

How could this be made to work without having to succumb to SameSite=None?

方法如下。因为 Lax 允许 top-level GET 但不允许 POST (这被认为是“危险的”),所以使用 GET 进行初始重定向而不是 POST.

自相矛盾的是,GET 可以说不如 POST 安全,但敏感数据(用户令牌)仅在最终重定向时发送回请求身份验证的站点 - 初始重定向只说“嘿,我正在请求身份验证”。

这是支持这种可能性的 a brief excerpt,结论是:

In conclusion, the IdP should continue to function when its cookies are being defaulted to SameSite=Lax by browsers (currently tested on Chrome 78-81 and Firefox 72 with the same-site default flags set). Typically, we have only seen the IdP itself break when the JSESSIONID is set to SameSite 'Strict', which should not happen apart from when explicitly trying to set SameSite=None with older versions of Safari on MacOS <=10.14 and all WebKit browsers on iOS <=12 (https://bugs.webkit.org/show_bug.cgi?id=198181). However with regards to achieving single-sign-on you may see degraded operation, and the following possibilities occur:

初始重定向需要在授权域上使用 cookie,而请求授权的域不请求 cookie - 它是 设置 基于 POST 给它。所以这应该在理论上与 Lax 一起工作,因为在最终的 POST 请求中不需要可用的 cookie - 只有初始请求。最终的 POST 重定向将无法在该请求上发送 cookie...但它 不需要 - 我们正在 POST 请求自身,并以此为基础设置 cookie。天才!

修改后的 SSO 流程

原始 SSO - 需要 SameSite=None:

  1. 请求者POST向授权提供者
  2. 授权提供商接收 cookie(需要 None 或未定义 SameSite)并创建临时令牌
  3. 授权提供商重定向返回使用令牌发送给请求者,令牌对其进行验证并创建会话 cookie

修订的 SSO - 兼容 SameSite=Lax:

  1. 请求者GET向授权提供者
  2. Auth 提供商接收 cookie(因为这是 GET,而不是 POST)并创建临时令牌
  3. Auth 提供者使用令牌重定向回请求者,后者对其进行验证并创建会话 cookie

一个区别 - 就是这样 - GET 初始重定向,而不是 POST。这是有效的,因为初始重定向不包含敏感信息。这个 POST 很可能是 GET。通过合并,我们可以提高整个会话 cookie 和任何记住我 cookie 的安全级别 - 不错!

我已经在 Chromium 70 和 Chrome 84 中使用严格标志和 third-party cookie 阻止进行了测试(所以没有“Lax + POST”,它只是“Lax ”)。这确实有效。您也可以将任何记住我的 cookie 设置为 SameSite=Lax - 如果身份验证器需要使用它们自发地创建会话,因为没有会话正在进行,只要存在重定向,这样做的 cookie 就可用GET 而不是 POST - 所以我们很好!

结论

SSO 可以Lax 一起工作。显然,XHR、动态 CSS、websockets 等不会,但它们可以在原始域后面被简单地代理。通过在初始重定向中使用 GET 而不是 POST,您可以转向使用带有 SameSite=Lax.

的 cookie

更复杂的 SSO 过程可能会有所不同 - 我在这里给出的只是一个非常简单的 SSO 示例。但是,SSO 和 SameSite=Lax 并非 相互不兼容 - 您可以通过稍微调整 SSO 设置使其工作,如果您根据需要进行其他更改,则不会出现任何问题。

请注意,您 可以 仍然与 SameSite=Strict 进行会话 - 如果您的整个站点都在一个主机名上并且高度敏感,我建议改为.但是,如果您需要进行 SSO,您至少可以使用 SameSite=Lax(当然不能使用 Strict)。