深入理解 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 应用程序。