CSRF漏洞攻击(跨站请求伪造攻击)

一、什么是CSRF攻击?

CSRF(跨站请求伪造)是一种攻击方式,攻击者诱骗受害者的浏览器向一个已登录的Web应用程序发送非本意的请求,从而在用户不知情的情况下,以用户的名义执行某些操作。

攻击者通过诱导用户访问恶意网站,使用户浏览器自动携带 Cookie 等身份凭证,向目标网站发起请求,执行非用户本意的操作如:转账、修改密码、发消息等

简单来说,就是攻击者利用了用户已经通过身份验证的会话,伪造请求来执行操作。

CSRF攻击的核心原理:

  • 用户已登录目标网站(如银行、社交平台、邮件系统),并保持了有效的会话(例如,Cookie未过期)。
  • 用户访问了攻击者控制的恶意网站,或者点击了恶意链接。
  • 恶意网站中包含一个指向目标网站的请求(如<img src="https://bank.com/transfer?to=attacker\&amount=1000">)。
  • 浏览器会自动携带用户在bank.com的Cookie(包括身份验证信息)发送该请求。
  • 目标服务器收到请求,认为是用户本人发起的合法操作,于是执行转账等操作。

案例1:

假设你登录了 mybank.com,然后在另一个标签页打开了一个恶意网站evil.com
evil.com 的页面上有一个隐藏的图片或自动向 mybank.com网站提交的表单:

或通过 JavaScript 发起的请求,其目标是 mybank.com 的敏感操作接口。

html 复制代码
--假设这是一个银行处理转账的请求链接
<img src="https://mybank.com/transfer?to=hacker&amount=5000" width="0" height="0">

你的浏览器会尝试加载这个"图片",并自动发送请求到mybank.com,同时带上你的登录Cookie

如果银行服务器看到这个来自已登录用户的 GET 请求处理转账,就执行了转账操作,而你完全不知情。攻击就可能成功。

案例2: .NET MVC CSRF 攻击案例:

假设你正在开发一个银行应用,其中有一个功能允许用户更改自己的邮箱地址。

csharp 复制代码
public class AccountController : Controller
{
    // 显示修改邮箱页面
    public ActionResult ChangeEmail()
    {
        return View();
    }

    // 处理邮箱修改
    [HttpPost]
    public ActionResult ChangeEmail(string newEmail)
    {
        if (ModelState.IsValid)
        {
            // 更新当前用户的邮箱
            UserService.UpdateUserEmail(User.Identity.Name, newEmail);
            ViewBag.Message = "邮箱已成功修改。";
        }
        return View();
    }
}

对应的视图 ChangeEmail.cshtml

html 复制代码
@using (Html.BeginForm("ChangeEmail", "Account"))
{
    <label>新邮箱:</label>
    <input type="text" name="newEmail" />
    <input type="submit" value="修改邮箱" />
}

CSRF 攻击过程

攻击者知道该网站存在一个 /Account/ChangeEmail 的 POST 接口用于修改邮箱,

并且没有验证 AntiForgeryToken。于是:

  • 用户登录了银行网站 https://bank.example.com
  • 用户在另一个浏览器标签中访问了攻击者的恶意网站:https://evil-site.com

恶意网站https://evil-site.com包含如下 HTML 代码:

html 复制代码
<html>
  <body>
    <h1>免费领取礼品!</h1>
    <form action="https://bank.example.com/Account/ChangeEmail" method="POST">
      <input type="hidden" name="newEmail" value="attacker@evil.com" />
      <input type="submit" value="点击领取" />
    </form>
    <script>
      document.forms[0].submit(); // 自动提交表单
    </script>
  </body>
</html>
  • 当用户加载此页面时,浏览器会自动向银行网站发送一个 POST 请求。
  • 由于用户仍处于登录状态,请求携带了有效的身份认证 Cookie(如 .ASPXAUTH)。
  • 银行服务器收到请求后,无法判断这是用户主动操作还是被诱导的,于是执行了邮箱修改操作。
  • 用户的邮箱被悄悄更改为 attacker@evil.com,攻击者可借此重置密码,完全控制账户。

二、如何防范CSRF攻击?

  • 使用CSRF Token(最常用):
    • 服务器在返回表单或页面时,嵌入一个随机生成的、一次性的Token。
    • 当用户提交表单时,必须包含这个Token。
    • 服务器验证Token的有效性,无效则拒绝请求。
    • 由于攻击者无法获取这个Token,因此无法伪造有效的请求。
  • 检查请求头中的 Referer 或 Origin 字段:
    • 服务器可以检查请求的来源是否合法。
    • 但此方法可能被绕过,且某些隐私设置会禁用Referer。
  • SameSite Cookie 属性:
    • 将Cookie的SameSite属性设置为 Strict 或 Lax。
    • SameSite=Lax:允许同站请求和部分安全的跨站GET请求。
    • SameSite=Strict:完全禁止跨站Cookie发送。
      这是现代浏览器推荐的防御手段之一。
  • 双重提交Cookie:
    • 将CSRF Token同时放在请求参数和Cookie中,服务器验证两者是否匹配。
    • 利用同源策略,攻击者无法读取Cookie中的Token。
  • 要求用户重新验证:
    • 对于敏感操作(如修改密码、大额转账),要求用户重新输入密码或进行二次验证。

三、防伪令牌(Anti-Forgery Token)使用案例

在 .NET Core 中,使用防伪令牌(Anti-Forgery Token)是防止跨站请求伪造(CSRF)攻击的标准做法

  • 1、启用 Anti-Forgery 支持(默认已启用)

    csharp 复制代码
    var builder = WebApplication.CreateBuilder(args);
    
    // 添加 MVC 服务(包含防伪令牌支持)
    builder.Services.AddControllersWithViews();
    
    var app = builder.Build();
    
    app.UseRouting();
    
    app.MapControllerRoute(
    	name: "default",
    	pattern: "{controller=Home}/{action=Index}/{id?}");
    
    app.Run();
  • 2、 在 Razor 视图中生成防伪令牌

    在需要提交表单的 .cshtml 视图中,使用 @Html.AntiForgeryToken() 或 :

    html 复制代码
    <form asp-action="Update" asp-controller="Home" method="post">
    	@Html.AntiForgeryToken()
    	<!-- 或者使用标签助手 -->
    	<!-- <input asp-antiforgery="true" /> -->
    
    	<input type="text" name="name" />
    	<button type="submit">提交</button>
    </form>

    这会在表单中生成一个隐藏输入字段,如:

    html 复制代码
    <input name="__RequestVerificationToken" type="hidden" value="CfDJ8s..." />
  • 3、在控制器中验证防伪令牌

    在处理 POST、PUT、DELETE 等敏感操作的 Action 上,使用 [ValidateAntiForgeryToken] 特性:

    csharp 复制代码
    [HttpPost]
    [ValidateAntiForgeryToken]
    public IActionResult Update(string name)
    {
    	// 验证通过后处理业务逻辑
    	// ...
    
    	return View();
    }

    或者,你也可以在类级别应用,以保护整个控制器的所有 POST 方法:

    csharp 复制代码
    //该特性仅对非 GET 请求生效。
    [ValidateAntiForgeryToken] 
    public class HomeController : Controller
    {
    	[HttpPost]
    	public IActionResult Update(string name) { ... }
    }
  • 4、 AJAX 请求中使用防伪令牌

    对于 AJAX 请求(如 jQuery 或 Fetch API),你需要手动提取令牌并附加到请求头或数据中。

    方法一:从页面提取令牌

    html 复制代码
    <!-- 在页面中预留令牌 -->
    <form id="antiForgeryForm" asp-antiforgery="true" style="display:none;"></form>
    javascript 复制代码
    function getAntiForgeryToken() {
    	return $('#antiForgeryForm input[name="__RequestVerificationToken"]').val();
    }
    
    // 使用 jQuery AJAX
    $.ajax({
    	url: '/Home/Update',
    	type: 'POST',
    	data: {
    		__RequestVerificationToken: getAntiForgeryToken(),
    		name: 'test'
    	},
    	success: function() { }
    });

    方法二:使用 RequestVerificationToken 请求头(推荐)

    将令牌放在请求头中更安全:

    javascript 复制代码
    $.ajax({
    	url: '/Home/Update',
    	type: 'POST',
    	headers: {
    		'RequestVerificationToken': getAntiForgeryToken()
    	},
    	data: { name: 'test' },
    	success: function() { }
    });

    然后在控制器中启用从请求头读取:

    csharp 复制代码
    // 可在 Program.cs 中配置
    builder.Services.AddAntiforgery(options =>
    {
    	options.HeaderName = "RequestVerificationToken";
    });
相关推荐
慧一居士3 小时前
CSS3 全部功能点介绍,使用场景,对应功能点完整使用示例
前端
烛阴3 小时前
深入Lua包(Package)与依赖管理
前端·lua
IT_陈寒3 小时前
5个Vue3性能优化技巧,让你的应用提速50% 🚀(附实测对比)
前端·人工智能·后端
god003 小时前
chromium项目中添加源文件(BUILD.gn项目中添加源文件)
java·服务器·前端
快乐非自愿4 小时前
Vue 缓存之坑,变量赋值方式和响应式数据
前端·vue.js·缓存
Github掘金计划4 小时前
别再用 “臃肿监控” 了!这款轻量监控神器开源 3 月狂揽 1.3k Star!
前端·监控
努力学习的少女4 小时前
SpaekSql函数
前端·数据库
摸着石头过河的石头4 小时前
错误处理:构建健壮的 JavaScript 应用
前端·javascript
w_y_fan4 小时前
flutter_native_splash: ^2.4.7
android·前端·flutter