.NET MVC中的防CSRF攻击
一.CSRF是什么?
CSRF(Cross-site request forgery),中文名称:跨站请求伪造,也被称为:one click attack/session riding,缩写为:CSRF/XSRF。它跟XSS(XSS又叫CSS:Cross-Site-Script)攻击不同,XSS一般是利用站内信任的用户在网站内插入恶意的脚本代码进行攻击,而CSRF则是伪造成受信任用户对网站进行攻击,攻击者能使用登陆用户的权限做一些该用户权限范围内的事。
CSRF这种攻击方式在2000年已经被国外的安全人员提出,但在国内,直到06年才开始被关注,08年,国内外的多个大型社区和交互网站分别 爆出CSRF漏洞,如:NYTimes.com(纽约时报)、Metafilter(一个大型的BLOG网站),YouTube和百度HI......而 现在,互联网上的许多站点仍对此毫无防备,以至于安全业界称CSRF为“沉睡的巨人”。
二.CSRF的原理
下图简单阐述了CSRF攻击的思想:
从上图可以看出,要完成一次CSRF攻击,受害者必须依次完成两个步骤:
1.登录受信任网站A,并在本地生成Cookie。
2.在不登出A的情况下,访问危险网站B。
看到这里,你也许会说:“如果我不满足以上两个条件中的一个,我就不会受到CSRF的攻击”。是的,确实如此,但你不能保证以下情况不会发生:
1.你不能保证你登录了一个网站后,不再打开一个tab页面并访问另外的网站。
2.你不能保证你关闭浏览器了后,你本地的Cookie立刻过期,你上次的会话已经结束。(事实上,关闭浏览器不能结束一个会话,但大多数人都会错误的认为关闭浏览器就等于退出登录/结束会话了......)
3.上图中所谓的攻击网站,可能是一个存在其他漏洞的可信任的经常被人访问的网站。
三、.Net MVC中提供的防跨CSRF攻击的方法
一个简单的栗子:
我们在登陆了网站A时,没有退出的情况下点击链接访问了网站B的如下页面(同源和不同源都可以,但是攻击者使用ajax则会受到同源限制),那么Id为1的账号就会被改掉密码和用户名,完成了简单的盗号。为什么 /Admins/UserInfo/EditUserInfo 只有登陆用户能够访问和操作,而攻击者没有用户的账号密码也完成了修改用户信息呢?这是因为网站A只通过Cookie(Session也是基于Cookie的)来记住用户。当发起一个对网站A的请求时,浏览器只负责找到网站A的Cookie,把Cookie一并发给网站A的服务器,服务器根据Cookie的SessionId获取用户信息,顺利验证成功,就有修改用户密码的权限了。
@{ ViewBag.Title = "DoSomeSthingBad"; } <script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.js"></script> <h2>你中招了哦~</h2> <form action="http://localhost:1911/Admins/UserInfo/EditUserInfo" method="post"> <input type="text" name="Id" value="1" /> <input type="text" name="UserName" value="testhk" /> <input type="text" name="Account" value="testhk" /> <input type="text" name="UserPass" value="456456" /> <button id="attack" type="submit">修改管理员</button> </form> <script> var attact = document.querySelector('#attack'); attack.click(); </script>
在.net mvc中防CSRF攻击十分简单步骤如下:
第一步:在form表单中添加 : @Html.AntiForgeryToken()
Html.AntiForgeryToken()方法会在input和Cookie中分别生成一个名为__RequestVerificationToken的token
第二步:在后台对应的Action上边加 [ValidateAntiForgeryToken]
这是一个验证过滤器,调用AntiForgery类的Validate方法,判断上边的两个token是否一致(不是相等),一致则通过。
第三步:把token发送到服务器
我们直接使用表单的submit时,会自动的将name为__RequestVerificationToken的input中的值直接提交,几乎不用添加任何操作,不再展示。下边时用普通ajax请求时将token发送到后台的方式:
var __RequestVerificationToken = $("input[name='__RequestVerificationToken']").val(); //layui中的用法,普通ajax中在ajax的data中新增一个字段即可 //如$.post(url,{Id:1,__RequestVerificationToken:__RequestVerificationToken},funtion(){..}) data.field.__RequestVerificationToken = __RequestVerificationToken; $.post('@Url.Action("EditUserInfo")', data.field, function (result) { if (result.IsSuccess === 1) { layer.msg(result.Msg, { icon: 1, time: 1000 }, function () { parent.layer.close(index); parent.layui.table.reload('testReload'); //重载表格 }); } else { layer.msg(result.Msg, {icon:2}); } });
参考文献:
1.https://www.cnblogs.com/soundcode/p/4884260.html
2.https://blog.csdn.net/cpytiger/article/details/8781457
3.https://www.cnblogs.com/wangyuyu/p/3388169.html
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【译】Visual Studio 中新的强大生产力特性
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
· 字符编码:从基础到乱码解决