揭秘 CORS、CSRF 令牌、SameSite 和点击劫持 - 网络安全
请阅读原文,其中包含各种修复方法!
互联网最棒的特性之一就是它的向后兼容性。但讽刺的是,这也使得互联网默认情况下存在一定的安全性问题。
了解不同的技术和攻击途径可能相当复杂。虽然互联网上充斥着大量正确的信息,但也充斥着大量零散、过时、错误或不完整的信息。
在我们深入探讨之前
本系列文章探讨 Web 安全,特别是浏览器安全。内容涵盖 CORS、CSRF 令牌、SameSite、点击劫持、httpOnly 和安全 cookie、XSS、CSP 等,http://以及所有可能与之相关的问题:SameSite=Lax 是否能消除 CSRF 令牌和/或 CORS?React/Vue 等框架真的能抵御所有 XSS 攻击吗?我还需要担心 JSON 劫持吗?我可以在单页应用 (SPA) 中使用 CSRF 令牌吗?等等🤯
💡 如果我遗漏了什么或者犯了错误,请在评论区告诉我。
在这篇文章中,我们将介绍 CORS、CSRF 令牌、SameSite、点击劫持和 JSON 劫持。
让我们深入探讨一下。
举例来说,假设你正在开发银行软件。我们重点关注两个终端:
- GET /accounts -> 列出用户帐户
- POST /transfer -> 转账
用户要使用您的软件,必须通过cookie登录。
整个安全困境源于您的某个用户被攻击者诱骗访问钓鱼网站。
为什么这很危险?因为截至 2021 年,大多数现代浏览器仍然会将所有 cookie 随请求一起发送。即使是在第三方网站(例如钓鱼网站)中也是如此。因此,由于用户在您的银行网站上拥有 cookie,所以向您的银行发送的请求也会同时发送这些用于识别用户的 cookie。
🤔 在钓鱼网站上,攻击者能否植入恶意 JavaScript 代码,通过简单的 AJAX 请求同时获取您的账户信息(GET 请求)和转账(POST 请求)?
答案是:不行!除非银行服务器明确允许。
CORS简介
CORS,即跨域资源共享,是一种浏览器安全功能,可防止在第三方环境中发出 AJAX 请求。
可以通过设置各种 HTTP 标头来调整此选项,因为有时确实需要允许某些第三方与服务器通信(例如,SPA 应用与 API 服务器之间的通信)。因此,如果银行不小心设置了错误的 CORS 标头,可能会造成严重问题。
🤔 CORS是如何保护用户免受AJAX请求的?
我们先来了解一下HTTP方法GET:
CORS机制会阻止攻击者访问JavaScript请求的响应。此外,您会在控制台中看到CORS违规错误。
但请注意,银行服务器对 GET 请求所做的任何操作都会实际执行!这是因为浏览器在收到响应之前无法识别 CORS 标头。
这通常不是问题,因为 GET 请求的作用仅仅是获取信息。你不应该在 GET 请求中执行任何破坏性操作,例如删除数据库条目。
🤔 POST、PUT、DELETE 和 PATCH 请求呢?这些都是破坏性操作,我需要担心吗?
不!在这些请求中,浏览器会首先使用 HTTP 方法发起一个所谓的预检请求OPTION。这个请求不会执行任何代码,只会返回 CORS 标头。然后,浏览器会判断是否可以安全地发送正式请求。
请注意,这之所以会成为问题,是因为浏览器中的 cookie 会被一起发送。因此,如果攻击者尝试从服务器脚本向您的银行发送 CURL 请求(此时 CORS 机制不适用),他也无法造成太大影响。(只要他没有您的 cookie……我们以后再详细讨论这一点)
TLDR;
- CORS 会阻止第三方上下文中的 AJAX 请求。
- 对于 GET 请求,它通过禁止 JavaScript 读取响应来实现这一点,但你的服务器端路由代码仍然需要先运行。
- 对于其他 HTTP 动词,它会先发送预检请求,
OPTION以避免对您的服务器执行任何破坏性操作。
但攻击者可能还有其他伎俩。
他们无需发起 API 请求,就可以<form />在钓鱼网站上放置一个指向您网站的链接,并自动提交。这样一来,攻击者就会直接跳转到您的网站(也就是说,浏览器会直接跳转到您的网站),同时发送 cookie。当然,这对于恶意操作来说非常危险,例如攻击我们银行的转账端点。
这被称为跨站请求伪造 (CSRF),由于它不是 AJAX 请求,而是通过顶级导航进行操作,因此 CORS 无法保护您免受其害。
引入 CSRF 令牌
这不是浏览器内置功能,而是解决此问题的常用方法。
工作原理如下:银行中的
每个人都必须包含一个类似这样的 CSRF 令牌:<form />
<form action="..." method="POST">
<input type="hidden" name="_csrf" value="C4N-U_R34D+T#15?">
<button>Submit</button>
</form>
当然,这个令牌并非完全硬编码,每次刷新页面时都会改变。
使用框架时,令牌通常像<form />这样添加到框架中:
<form action="..." method="POST">
{{ csrfField() }}
<button>Submit</button>
</form>
现在,当攻击者将该网站放在<form />他的钓鱼网站上时,他将没有 CSRF 令牌,因此表单提交将始终失败并返回错误403 forbidden。
🤔 你可能会问,攻击者难道不能使用服务器脚本(绕过 CORS)来获取银行网站的任何内容<form />,从 HTML 中提取 CSRF 令牌,将其放入恶意表单中并顺利提交吗?
答案是:不行!因为 CSRF 令牌与用户会话绑定。所以你的CSRF 令牌对我无效。
如果您未使用框架,而是使用独立库来实现 CSRF 令牌,则必须确保正确集成它们以防止上述情况发生。
🤔 既然我已经有了 CSRF 令牌,还需要 CORS 吗?
是的!虽然攻击者也需要你的 CSRF 令牌才能发起 AJAX 请求,但他不需要它就能发起 GET 请求来获取你的账户信息。理论上,你可以用令牌保护 GET 请求,但通常情况下,你希望用户能够直接访问你的网站/accounts,将其添加到书签,稍后再打开,而无需在 URL 中包含任何会在短时间内过期的令牌。
🤔 我只通过 API 与服务器通信,而不是通过浏览器的<form />元素。我需要注意吗?
是的,因为攻击者仍然可以<form />在他的恶意网站上放置一个指向你 API 端点的操作。你是否使用 API 并不重要。
TLDR;
<form />CSRF令牌是一种常见的自定义解决方案,用于防止CSRF攻击,它通过元素和顶级导航来实现,而不是通过AJAX。- 它们与用户的会话相关联。
- 它们还会阻止 POST、PUT、PATCH 和 DELETE 类型的 AJAX 请求,但通常不会阻止 GET 请求。请继续使用 CORS!
坦白说,每个网站都必须实现 CSRF 令牌,这确实挺烦人的。虽然我的解释(希望如此 :D)很简单,但实际上存在一些复杂情况,例如单页应用程序 (SPA)、令牌过期等等。
如果浏览器在第三方环境下(无论是否使用 AJAX)都不发送 cookie,那岂不是很好吗?
SameSite 简介
SameSite 是一个 cookie 属性,您可以使用它来指定何时应将 cookie 与请求一起发送。
可以设置为:
- 无:无论上下文如何,Cookie 都将始终发送。这仅适用于带有“安全”标志的 Cookie。
- Lax:在第三方上下文中,AJAX 请求以及使用 POST 方法的顶级导航(请求)将不会发送 cookie。这正是我们想要的!
- 严格模式:与宽松模式相同,但对于使用 GET 方法进行的顶级导航,也不会发送 cookie。
听起来很不错,对吧!好消息是,浏览器已经开始将 SameSite=Lax 设置为默认选项,从而为网络提供更高的安全性。希望所有厂商都能尽快实现这一点。
如果您对“严格”感到困惑……它真的非常严格。这意味着如果您点击网站 A 上的链接跳转到网站 B(您已登录),cookie 将不会被发送,您也不会在网站 B 上登录(您必须刷新页面或点击网站 B 上的任何链接才能重新登录)。
那么,让我们来解答几个常见问题吧!
🤔 什么是“同一”网站?
基本上是指根域名(顶级域名及其前面的部分)。所以,如果您http://client.bank.com同时拥有 `example.com` 和https://www.api.bank.com`example.com`,它们仍然被视为“同名网站”。
🤔 等等,那像 GitHub Pages 这样的网站呢?它们都属于github.io……
有一个公开的后缀列表可以用来修复这些问题。
🤔 CORS 代表“跨域请求”。“源”和“站点”有什么区别?
我们之前已经解释过“站点”的含义。“源”的定义则严格得多。两个站点必须具有相同的协议(HTTP/HTTPS)、端口和子域名。
更多信息请访问:https://web.dev/same-site-same-origin/
🤔 当 SameSite=Lax/Strict 时,我们还需要 CSRF 令牌吗?
这要视情况而定。SameSite 是一项比较新的功能,旧版浏览器甚至一些较旧版本的现代浏览器都不支持此功能。如果可以,请阻止旧版浏览器访问您的网站。
🤔 我们还需要对 CORS 保持警惕吗?
如果你支持非最新版本的浏览器:是的!
如果不是:可能……,但为什么要冒这个险呢?
SameSite 会阻止在第三方上下文中发送 cookie。因此,如果您禁用 CORS,攻击者就可以在浏览器中发送不包含 cookie 的 AJAX 请求,就像他们可以从服务器脚本发送 CURL 请求一样。听起来我们不再需要 CORS 了,对吧?
别忘了一点:网络并非只依赖 cookie 运行。网站还可以根据其他因素(例如你的 IP 地址)提供不同的信息。或者,浏览器可能返回包含敏感数据的缓存结果。
我实在看不出冒这个险有什么好处。与复杂的 CSRF 令牌不同,CORS 只是一个默认启用的 HTTP 标头。
TLDR;
- cookie 属性
SameSite=Lax可以防止 cookie 在第三方上下文中发送(包括 API 请求和顶级导航 POST 请求),从而使 CSRF 令牌过时。 - 旧版浏览器不支持此功能。
- 今后,
SameSite=Lax除非明确设置为其他值,否则将以此为默认值。
好的,所以我们可以阻止<form />第三方环境下的 Ajax 请求和提交。但是,如果攻击者采取其他方法呢?
他们没有提交表格,而是:
- 在 iframe 中加载银行网站
- 通过各种 CSS 规则使其不可见
- 将一个按钮绝对定位到 iframe 上方,使其位于 iframe 内的表单提交按钮之上。
- 给按钮添加 CSS 规则“pointer-events: none”,这样点击事件就会传递到它下面的元素(iframe)。
现在点击攻击者提供的按钮就会在不可见的 iframe 中提交表单 :/
点击劫持
这种攻击叫做点击劫持,你可以发挥很多创意,比如让用户以为自己在玩“打地鼠”游戏,而实际上,他是在为你浏览购物网站。
如何防止点击劫持?
如果 SameSite 配置正确,即使是 iframe,cookie 也不会在第三方上下文中传递。因此,如果网站使用 cookie 进行身份验证,这将大大提高安全性。
但攻击者仍然可以诱骗你先登录,然后再进行其他操作……
您可以通过设置以下 HTTP 标头来阻止您的页面通过 iframe 嵌入:
X-Frame-Options: DENY
但有时,你希望你的页面可以像社交媒体的点赞按钮一样嵌入到其他平台。
直到最近,唯一安全的方法是不使用 iframe,而是使用打开弹出窗口的链接。
但是,借助现代 JavaScript,即使在 iframe 中,您也可以使用交集观察器来检测您的网站是否 100% 可见。
要了解交叉路口观察器的实际工作原理以及更多相关细节,我强烈建议观看以下视频:https://www.youtube.com/watch?v =EIH6IQgwdAc
TLDR;
- 点击劫持是一种攻击手段,它诱骗用户点击嵌入在(不可见的)iframe 中的网站链接。
- 您可以通过将标头“X-Frame-Options”设置为“DENY”来阻止您的网站通过 iframe 嵌入。
- 对于现代浏览器,您可以使用交叉观察器来确保您的网站(在 iframe 中)确实可见。
在结束这篇文章之前,还有一种攻击途径需要注意,那就是 JSON 劫持。
JSON劫持
这在现代浏览器中不是问题,但随着 JavaScript 的新增功能,这个问题可能会再次出现。
它基本上允许你通过标签加载 API <script>(CORS 不适用的地方)来绕过浏览器中的 CORS,如果 SameSite 不适用,则同时发送 cookie。
通常情况下,这段代码只会执行脚本。但通过重写,Array你可以监听原生数组构造。因此,如果返回的是一个数组,你就可以读取它的内容。
这就是为什么 Facebook 的所有 API 请求都以无限for循环开始的原因。这样浏览器就永远无法执行数组部分。
这个故事还有一些更有趣的部分,你可以在这里找到更多细节。
下次,我们来探讨一下 CSRF 令牌是否可以在 SPA 中使用这个显而易见的问题。
文章来源:https://dev.to/michi/demystifying-cors-csrf-tokens-samesite-clickjacking-web-security-20n9