






















Go 1.25 在标准库中引入了一个新的中间件 http.CrossOriginProtection。这让我思考:我们是否终于可以在不依赖基于令牌的检查(如双重提交 Cookie)的情况下防止 CSRF 攻击?是否可以在不引入第三方包(如 justinas/nosurf 或 gorilla/csrf)的情况下构建安全的 Web 应用?
答案是一个谨慎的 “是” —— 只要满足一些重要条件。
该中间件通过检查请求中的 Sec-Fetch-Site 和 Origin 头来判断请求来源。它会自动拒绝来自非同源的 非安全请求(如 POST、PUT),并返回 403 Forbidden。
Sec-Fetch-Site 头。same-origin。http.CrossOriginProtection 会拒绝请求。Sec-Fetch-Site,则回退检查 Origin 与 Host 是否匹配。1 | package main |
如果您愿意,也可以配置 http.CrossOriginProtection 的行为。配置选项包括能够添加受信任的来源(允许来自这些来源的跨域请求),以及能够为被拒绝的请求使用自定义处理程序,而不是默认的 403 Forbidden 响应。
想自定义行为时,可以使用如下模式:
文件: main.go
1 | package main |
http.CrossOriginProtection 的主要限制是它只对阻止来自现代浏览器的请求有效。您的应用程序仍然容易受到来自不包含 Sec-Fetch-Site 或 Origin 头(通常是 2020 年之前的)的旧版浏览器的 CSRF 攻击。
目前,浏览器对 Sec-Fetch-Site 头的支持率为 92%,对 Origin 头为 95%。因此,通常情况下,仅仅依靠 http.CrossOriginProtection 不足以作为您唯一的 CSRF 防护措施。
还需要注意的是,只有当您的应用程序具有“可信来源”时才会发送 Sec-Fetch-Site 头——这基本上意味着您的应用程序在生产环境中使用 HTTPS(或开发期间使用 localhost),http.CrossOriginProtection 才能充分发挥作用。
您还应该知道,当请求中不存在 Sec-Fetch-Site 头,并且回退到比较 Origin 和 Host 头时,Host 头不包含方案。这个限制意味着当不存在 Sec-Fetch-Site 头但存在 Origin 头时,http.CrossOriginProtection 会错误地允许从 http://{host} 到 https://{host} 的跨域请求。为了减轻这种风险,您理想情况下应该配置您的应用程序使用 HTTP 严格传输安全 (HSTS)。
深入研究这个问题让我开始思考… 如果您已经计划使用 HTTPS 并强制使用 TLS 1.3 作为最低支持的 TLS 版本呢?您能否确信所有支持 TLS 1.3 的网络浏览器也支持 Sec-Fetch-Site 或 Origin 头之一呢?
据我从 MDN 兼容性数据和 Can I Use 网站的表格来看,答案是“是”,适用于(几乎)所有主流浏览器。
如果您强制使用 TLS 1.3 作为最低版本:
Sec-Fetch-Site 或 Origin 头之一——因此 http.CrossOriginProtection 将有效工作。我能看到的唯一例外是 Firefox v60-69 (2018-2019),它不支持 Sec-Fetch-Site 头,并且不为 POST 请求发送 Origin 头。这意味着 http.CrossOriginProtection 将无法有效阻止来自该浏览器的请求。Can I Use 显示 Firefox v60-69 的使用率为 0%,因此这里的风险似乎非常低——但世界上某个地方可能仍然有一些计算机在运行它。
此外,我们只掌握了主流浏览器(Chrome/Chromium、Firefox、Edge、Safari、Opera 和 Internet Explorer)的信息。但当然,还存在其他浏览器。它们大多数是 Chromium 或 Firefox 的分支,因此很可能没问题,但这里没有保证,并且很难量化风险。
因此,如果您使用 HTTPS 并强制使用 TLS 1.3,这是确保 http.CrossOriginProtection 有效工作的一大步。然而,Firefox v60-69 和非主流浏览器仍然存在非零风险,因此您可能希望增加一些纵深防御并同时使用 SameSite Cookie。
我们稍后会更多地讨论 SameSite Cookie,但首先我们需要快速绕道讨论“源 (origin)”和“站点 (site)”这两个术语之间的区别。
在 Web 规范和 Web 浏览器的世界中,跨站点 (cross-site) 和跨源 (cross-origin) 是细微不同的概念,在这样的安全上下文中,理解它们之间的区别并确切地表达我们的意思非常重要。
我将快速解释。
如果两个网站共享完全相同的方案 (scheme)、主机名 (hostname) 和端口(如果存在),则它们具有相同的源 (origin)。因此 https://example.com 和 https://www.example.com 不是相同的源,因为主机名 (example.com 和 www.example.com) 不同。它们之间的请求将是跨源的。
如果两个网站共享相同的方案和可注册域 (registerable domain),则它们是“同站 (same site)”的。
注意:可注册域是主机名中紧邻(并包括)有效顶级域 (effective TLD) 的部分。以下是一些示例:
https://www.google.com/,顶级域是 com,可注册域是 google.com。https://login.mail.ucla.edu,顶级域是 edu,可注册域是 ucla.edu。https://www.gov.uk,顶级域是 gov.uk,可注册域是 www.gov.uk。您可以在此处找到有效顶级域的完整列表。
因此,https://example.com、https://www.example.com 和 https://login.admin.example.com 都被认为是同站的,因为方案 (https) 和可注册域 (example.com) 相同。它们之间的请求不会被认为是跨站的,但会是跨源的。
注意:某些浏览器版本使用不同的同站定义,它不要求相同的方案,只要求相同的可注册域。对于这些浏览器版本,https://admin.example.com 和 http://blog.example.com 也将被视为同站。
如今,这通常被称为无方案同站 (schemaless same-site),但在历史版本或文档中,它可能只被称为同站。
那么,我在这里想要表达的要点是什么呢?
Go 的 http.CrossOriginProtection 中间件的命名是准确且恰当的。它阻止跨源请求。它比只阻止跨站请求更严格,因为它也阻止来自同一站点(即可注册域下的其他源)的请求。
这很有用,因为它有助于防止您的老旧、十多年未更新的 WordPress 博客 https://blog.example.com 被攻破,并被用来向您的重要网站 https://admin.example.com 发起请求伪造攻击的情况。
当大多数人——包括我自己在内——随意谈论“CSRF 攻击”时,我们大多数时候指的是实际上是跨源请求伪造 (cross-origin request forgery),而不仅仅是跨站请求伪造 (cross-site request forgery)。很遗憾 CSRF 是描述这类攻击的常用和已知缩写,因为大多数时候 CORF 会更准确和恰当。但嘿!这就是我们所处的混乱世界。
然而,在本文的其余部分,当我的意思确实是跨源请求伪造时,我将使用 CORF 代替 CSRF。
SameSite Cookie 属性自 2017 年起普遍受到网络浏览器的支持,Go 自 v1.11 起也支持。如果您在 Cookie 上设置 SameSite=Lax 或 SameSite=Strict 属性,则该 Cookie 将仅包含在发送到设置它的同一站点的请求中。反过来,这可以防止跨站请求伪造攻击(但不能防止来自同一站点内的跨源攻击)。
这里有一些好消息——所有支持 TLS 1.3 的主流浏览器也都完全支持 SameSite Cookie,据我所知没有例外。因此,如果您强制使用 TLS 1.3,您可以确信所有使用您应用程序的主流浏览器都会遵守 SameSite 属性。
这意味着通过在 Cookie 上使用 SameSite=Lax 或 SameSite=Strict,您可以消除我们之前谈到的 Firefox v60-69 引起的跨站请求伪造风险。
如果您结合使用 HTTPS,强制将 TLS 1.3 作为最低版本,适当使用 SameSite=Lax 或 SameSite=Strict Cookie,并在您的应用程序中使用 http.CrossOriginProtection 中间件,据我所知,主流浏览器只剩下两个未被缓解的 CSRF/CORF 风险:
Sec-Fetch-Site 头的浏览器。对于第一个风险,如果您在可注册域下没有任何其他网站,或者您确信这些网站是安全的且未被攻破,那么鉴于 Firefox v60-69 的使用率极低,这可能是一个您愿意接受的风险。
对于第二个风险,如果您的源根本不支持 HTTP(包括重定向),那么您无需担心这一点。否则,您可以通过在 HTTPS 响应中包含 HSTS 头来缓解风险。
在本文的开头,我提到在某些条件下不使用基于令牌的 CSRF 检查可能是可以的。那么,让我们回顾一下这些条件是什么:
http.CrossOriginProtection 中间件和 SameSite=Lax 或 SameSite=Strict Cookie。使用 SameSite Cookie 对于一般的纵深防御很重要,更具体地说,是为了缓解来自 Firefox v60-69 的 CSRF 攻击。此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。