简体中文
  • csrf 攻击
  • web 安全
  • 跨站请求伪造
  • cookie 安全
  • 同源策略
  • csrf 预防
  • SameSite

深入理解 CSRF

深入探讨跨站请求伪造 (CSRF) 攻击,解释其机制,演示实例,并详细说明各种预防方法,以增强 Web 应用程序的安全性。

Yijun
Yijun
Developer

在进行 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。此过程是自动的,不需要任何 JavaScript 代码或用户交互。

此机制使得网站能够轻松地记住用户的登录状态,因为每个请求会自动携带用户的身份信息。 加粗 例如,当你登录银行网站 (bank.com) 并获得一个身份 Cookie,那么当你点击查看账单时,浏览器会自动找到所有匹配 bank.com 的 Cookie 并将它们附加到账单请求中。银行服务器可以在后台识别你并返回你的账单信息。

CSRF 攻击步骤

  1. 用户登录目标网站(如银行网站)并获得身份认证 Cookie。 这一步利用自动 Cookie 发送机制。在银行网站设置身份认证 Cookie 后,浏览器会自动将此 Cookie 附加到发送给该网站的每个请求中。

  2. 用户在未退出的情况下访问恶意网站。 此时,由于同源策略,恶意站点不能直接读取或修改银行站点的 Cookie。这保护用户的身份信息不被直接窃取。

  3. 恶意站点包含向目标站点的请求(如转账操作)。 虽然同源策略限制跨来源访问,但它允许跨来源网络请求,如通过 <img><form> 标签发起的请求。攻击者利用这个“漏洞”。

  4. 用户的浏览器自动发送此请求,并附带目标站点的 Cookie。 这是 CSRF 攻击的核心。它利用了同源策略允许跨来源请求和自动 Cookie 发送机制(即使是恶意站点触发的请求也会携带匹配域的 Cookie)。

  5. 目标站点接收请求,验证 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 攻击最常用且最有效的方法之一。其工作原理如下:

  1. 服务器为每个会话生成一个唯一且难以预测的令牌。
  2. 该令牌嵌入到所有需要的表单中。
  3. 当用户提交表单时,服务器验证令牌的有效性。

由于 CSRF 令牌是绑定到用户会话的唯一值,而攻击者无法获悉或猜测该值(因为每个会话都不同),即使攻击者欺骗用户发送请求,服务器也会因缺少有效的 CSRF 令牌而拒绝该请求。

实现示例:

在服务器端(使用 Node.js 和 Express):

在前端 JavaScript 中:

检查 Referer 标头

Referer 标头包含发起请求的页面的 URL。通过检查 Referer 标头,服务器可以判断请求是否来自合法来源。

由于 CSRF 攻击通常来自不同的域,Referer 标头将显示攻击者的域名。通过验证 Referer 是否为预期值,可以阻止来自未知来源的请求。

但是需要注意的是,此方法并不完全可靠,因为某些浏览器可能不会发送 Referer 标头,用户也可以通过浏览器设置或插件禁用 Referer 标头。

实现示例:

SameSite 是一个 Cookie 属性, 用于控制 Cookie 是否随着跨站请求发送。它有三个可能的值:

  • Strict: Cookie 仅在同站请求中发送。
  • Lax: Cookie 在同站请求和顶级导航中发送。
  • None: Cookie 在所有跨站请求中发送(必须与 Secure 属性一起使用)。

SameSite 设置为 Strict 时,可以完全防止第三方网站发送 Cookie,从而有效防止 CSRF 攻击。 如果 SameSite 设置为 Lax,可以在保护敏感操作的同时,允许一些常见的跨站用例(如从外部链接进入网站)。

实现示例:

使用自定义请求头

对于 AJAX 请求,可以添加自定义请求头。由于同源策略的限制,攻击者无法在跨站请求中设置自定义头。服务器可以检查此自定义头的存在以验证请求的合法性。

实现示例:

在前端:

在服务器端:

双重 Cookie 验证是一种有效的 CSRF 防御技术。其核心原理是服务器生成一个随机令牌,将其设置为 Cookie,并将其嵌入页面(通常作为隐藏表单字段)。当浏览器发送请求时,它会自动包括 Cookie,而页面的 JavaScript 会将令牌作为请求参数发送。服务器随后验证 Cookie 中的令牌是否与请求参数中的令牌匹配。

虽然攻击者可以将目标网站的 Cookie 包含在跨站请求中,但他们无法读取或修改 Cookie 的值,也无法访问或修改页面中的令牌值。通过要求请求同时包含来自 Cookie 和参数的令牌,确保请求来自具有读取 Cookie 权限的来源,从而有效地抵御 CSRF 攻击。

对于敏感操作使用重新验证

对于特别敏感的操作(如更改密码或进行大额转账),可以要求用户重新验证身份。 这为用户提供了额外的安全检查点。即使用户成功发起了 CSRF 攻击,他们也无法通过重新验证步骤。

实现建议:

  • 在执行敏感操作之前,重定向到单独的身份验证页面。
  • 在此页面上,要求用户输入密码或其他身份验证信息。
  • 验证通过后,生成一次性令牌并在后续敏感操作中使用此令牌。

总结

通过这次深入讨论,希望你现在对 CSRF 攻击有了更全面的理解。 我们不仅学习了 CSRF 的工作原理,还探索了各种有效的防御措施。所有这些方法都能有效增强 Web 应用程序的安全性。

希望这些知识能帮助你在日常开发中更好地处理与 CSRF 相关的问题,并构建更安全的 Web 应用程序。