深入理解 CSRF
深入探讨跨站请求伪造 (CSRF) 攻击,解释其机制,演示实例,并详细说明各种预防方法,以增强 Web 应用程序的安全性。
在进行 Web 开发时,尤其是与 Cookie 相关的开发中,我们经常听到诸如“这个设置有助于防止 CSRF”之类的说法。然而,许多人对“CSRF”的真正含义只是模糊的。
今天,我们将深入了解 CSRF(跨站请求伪造),这是一个常见的 Web 安全漏洞。这将帮助我们更有效地处理与 CSRF 相关的问题。
什么是 CSRF?
CSRF(跨站请求伪造)是一种 Web 攻击,攻击者通过让已认证的用户执行未预期的操作来实现攻击。简单来说,就是“黑客冒充用户执行未经授权的操作”。
CSRF 如何工作
要理解 CSRF,我们需要掌握几个关键概念:
浏览器的同源策略
同源策略是浏览器中的一个安全特性,它限制一个来源的文档或脚本与另一个来源的资源交互。
一个来源由协议(如 HTTP 或 HTTPS)、域名和端口号组成。例如,https://example.com:443
是一个来源,而 https://demo.com:80
是另一个来源。
同源策略限制不同来源页面之间的数据访问,意味着:
- 一个来源的 JavaScript 不能读取另一个来源的 DOM
- 一个来源的 JavaScript 不能读取另一 个来源的 Cookie、IndexedDB 或 localStorage
- 一个来源的 JavaScript 不能向另一个来源发送 AJAX 请求(除非使用 CORS)
然而,为了保持 Web 的开放性和互操作性(如从 CDN 加载资源或向第三方 API 发送请求以进行日志记录),同源策略不限制跨来源网络请求:
- 页面可以向任何来源发送 GET 或 POST 请求(如加载图片或提交表单)
- 任何来源的资源都可以被包括在内(如
<script>
、<img>
、<link>
、<iframe>
标签)
自动 Cookie 发送机制
自动 Cookie 发送机制是浏览器的一个重要特性。当浏览器向域发送请求时,它会自动附带该域的所有 Cookie。此过程是自动的,不需要任何 JavaScript 代码或用户交互。
此机制使得网站能够轻松地记住用户的登录状态,因为每个请求会自动携带用户的身份信息。
加粗
例如,当你登录银行网站 (bank.com
) 并获得一个身份 Cookie,那么当你点击查看账单时,浏览器会自动找到所有匹配 bank.com
的 Cookie 并将它们附加到账单请求中。银行服务器可以在后台识别你并返回你的账单信息。
CSRF 攻击步骤
-
用户登录目标网站(如银行网站)并获得身份认证 Cookie。 这一步利用自动 Cookie 发送机制。在银行网站设置身份认证 Cookie 后,浏览器会自动将此 Cookie 附加到发送给该网 站的每个请求中。
-
用户在未退出的情况下访问恶意网站。 此时,由于同源策略,恶意站点不能直接读取或修改银行站点的 Cookie。这保护用户的身份信息不被直接窃取。
-
恶意站点包含向目标站点的请求(如转账操作)。 虽然同源策略限制跨来源访问,但它允许跨来源网络请求,如通过
<img>
、<form>
标签发起的请求。攻击者利用这个“漏洞”。 -
用户的浏览器自动发送此请求,并附带目标站点的 Cookie。 这是 CSRF 攻击的核心。它利用了同源策略允许跨来源请求和自动 Cookie 发送机制(即使是恶意站点触发的请求也会携带匹配域的 Cookie)。
-
目标站点接收请求,验证 Cookie 有效并执行操作。 服务器无法判断此请求是否来自合法用户操作,因为附带的 Cookie 是有效的。
CSRF 攻击示例
让我们用一个具体的示例来说明 CSRF 攻击如何发生。我们将以一个虚构的银行网站 bank.com
为例。
首先,用户访问 https://bank.com
并登录他们的账户。
登录成功后,服务器设置一个身份认证 Cookie,例如:
用户在银行网站上执行一个转账操作,比如转账 1000 美元给 Alice。此操作可能会发送一个如下请求:
现在,假设攻击者创建了一个包含以下 HTML 的恶意网站 https://evil.com
:
当用户点击 https://evil.com
链接而未退出银行账户时,因为他们已经登录 bank.com
,浏览器拥有一个有效 session_id
Cookie。
恶意页面加载后,会自动提交隐藏表单,向 https://bank.com/transfer
发送转账请求。
用户的浏览器会自动将 bank.com
Cookie 附带到此请求中。bank.com
服务器接收到请求,验证 Cookie 有效,然后执行此未经授权的转账操作。
预防 CSRF 攻击的常用方法
以下是几种常用的 CSRF 防御方法。我们将详细解释每种方法的原理以及其如何有效地防止 CSRF 攻击:
使用 CSRF 令牌
CSRF 令牌是防御 CSRF 攻击最常用且最有效的方法之一。其工作原理如下:
- 服务器为每个会话生成一个唯一且难以预测的令牌。
- 该令牌嵌入到所有需要的表单中。
- 当用户提交表单时,服务器验证令牌的有效性。
由于 CSRF 令牌是绑定到用户会话的唯一值,而攻击者无法获悉或猜测该值(因为每个会话都不同),即使攻击者欺骗用户发送请求,服务器也会因缺少有效的 CSRF 令牌而拒绝该请求。
实现示例:
在服务器端(使用 Node.js 和 Express):
在前端 JavaScript 中:
检查 Referer
标头
Referer
标头包含发起请求的页面的 URL。通过检查 Referer
标头,服务器可以判断请求是否来自合法来源。
由于 CSRF 攻击通常来自不同的域,Referer
标头将显示攻击者的域名。通过验证 Referer
是否为预期值,可以阻止来自未知来源的请求。
但是需要注意的是,此方法并不完全可靠,因为某些浏览器可能不会发送 Referer 标头,用户也可以通过浏览器设置或插件禁用 Referer 标头。
实现示例:
使用 SameSite
Cookie 属性
SameSite
是一个 Cookie 属性, 用于控制 Cookie 是否随着跨站请求发送。它有三个可能的值:
Strict
: Cookie 仅在同站请求中发送。Lax
: Cookie 在同站请求和顶级导航中发送。None
: Cookie 在所有跨站请求中发送(必须与Secure
属性一起使用)。
当 SameSite
设置为 Strict
时,可以完全防止第三方网站发送 Cookie,从而有效防止 CSRF 攻击。
如果 SameSite
设置为 Lax
,可以在保护敏感操作的同时,允许一些常见的跨站用例(如从外部链接进入网站)。
实现示例:
使用自定义请求头
对于 AJAX 请求,可以添加自定义请求头。由于同源策略的限制,攻击者无法在跨站请求中设置自定义头。服务器可以检查此自定义头的存在以验证请求的合法性。
实现示例:
在前端:
在服务器端:
双重 Cookie 验证
双重 Cookie 验证是一种有效的 CSRF 防御技术。其核心原理是服务器生成一个随机令牌,将其设置为 Cookie,并将其嵌入页面(通常作为隐藏表单字段)。当浏览器发送请求时,它会自动包括 Cookie,而页面的 JavaScript 会将令牌作为请求参数发送。服务器随后验证 Cookie 中的令牌是否与请求参数中的令牌匹配。
虽然攻击者可以将目标网站的 Cookie 包含在跨站请求中,但他们无法读取或修改 Cookie 的值,也无法访问或修改页面中的令牌值。通过要求请求同时包含来自 Cookie 和参数的令牌,确保请求来自具有读取 Cookie 权限的来源,从而有效地抵御 CSRF 攻击。
对于敏感操作使用重新验证
对于特别敏感的操作(如更改密码或进行大额转账),可以要求用户重新验证身份。 这为用户提供了额外的安全检查点。即使用户成功发起了 CSRF 攻击,他们也无法通过重新验证步骤。
实现建议:
- 在执行敏感操作之前,重定向到单独的身份验证页面。
- 在此页面上,要求用户输入密码或其他身份验证信息。
- 验证通过后,生成一次性令牌并在后续敏感操作中使用此令牌。
总结
通过这次深入讨论,希望你现在对 CSRF 攻击有了更全面的理解。 我们不仅学习了 CSRF 的工作原理,还探索了各种有效的防御措施。所有这些方法都能有效增强 Web 应用程序的安全性。
希望这些知识能帮助你在日常开发中更好地处理与 CSRF 相关的问题,并构建更安全的 Web 应用程序。