ASP.NET Core 防止跨站请求伪造(XSRF/CSRF)攻击
CSRF(Cross-site request forgery)跨站请求伪造,也被称为“One Click Attack”或者Session Riding,通常缩写为CSRF或者XSRF,是一种对网站的恶意利用。尽管听起来像跨站脚本(XSS),但它与XSS非常不同,XSS利用站点内的信任用户,而CSRF则通过伪装成受信任用户的请求来利用受信任的网站。与XSS攻击相比,CSRF攻击往往不大流行(因此对其进行防范的资源也相当稀少)和难以防范,所以被认为比XSS更具危险性。
这幅图已经能够很好地解释了跨站攻击是怎么回事,这样的网站很容易被别人钓鱼,
示例:
创建2个网站
officialsite站点里有个 pay页面
view:
<h1>支付</h1>
<form action="/pay/post" method="post">
<input name="user" placeholder="姓名" />
<input name="price" placeholder="金额" />
<button type="submit">提交</button>
</form>
paycontroller:
[HttpPost]
public IActionResult Post(string user, decimal price)
{
return Json(new { ok = true, msg = $"{user}支付成功{price}元" });
}
FakeSite 站点里有个fakepay页面
view:
<h1>假的支付</h1>
<form action="https://localhost:44316/pay/post" method="post">
<input name="user" placeholder="姓名" />
<input name="price" placeholder="金额" />
<button type="submit">提交</button>
</form>
提交的请求并非自己站点 而是上一个站点的接口
- 运行效果
界面 | 运行效果 |
---|---|
点击提交后一样返回了上面网站的请求结果,这样官网就太危险了。 |
那么asp.net core mvc里怎么处理?
CSRF能够成功是因为在同一个浏览器中Cookies是共享的,也就无法通过cookies权限认证和验证来防止。
解决的办法就是:要确保请求是自己的站点发出的就可以了,其他的站点发来的请求阻挡掉。
通过AntiForgeryToken 在页面生成一个Token,发请求的时候把Token带上。处理服务端处理请求的时候需要验证这个Token。而其他站点没有这个token那么也就没法通过验证,就被阻挡了。
- 继续改造上面的demo:
<form action="/pay/post" method="post">
@Html.AntiForgeryToken()
<input name="user" placeholder="姓名" />
<input name="price" placeholder="金额" />
<button type="submit">提交</button>
</form>
F12看生成的html
<h1>支付</h1>
<form action="/pay/post" method="post">
<input name="__RequestVerificationToken" type="hidden" value="CfDJ8Efjwr3EXZVEmlMrBkJrUWtWFC05YY78yz-ce3W4IjEE5m7SzISCXShbON9col2WEDQiqSuKJMbjnUV94a7nwO3O9YXHaFGJMl04AdDpviAGK8DidGgu9d7Gpbm8Zncd01AWL4v274emVQgJTabzPfo" />
<input name="user" placeholder="姓名" />
<input name="price" placeholder="金额" />
<button type="submit">提交</button>
</form>
controller
[ValidateAntiForgeryToken]
[HttpPost]
public IActionResult Post(string user, decimal price)
{
return Json(new { ok = true, msg = $"{user}支付成功{price}元" });
}
运行后界面一切正常
接下来我们在取运行上一个伪造的站点,什么都不改,提交后就会提示返回400的错误
这样就能搞定了。
以上是表单提交,项目中常用的还是ajax请求,那该如何处理呢
view:
@inject Microsoft.AspNetCore.Antiforgery.IAntiforgery csrf
<form action="/pay/post" method="post">
@Html.AntiForgeryToken()
<input name="user" placeholder="姓名" />
<input name="price" placeholder="金额" />
<button type="submit">提交</button>
<button type="button" id="btnajax">ajax提交</button>
</form>
<script type="text/javascript">
$(function () {
$("#btnajax").click(function () {
$.ajax({
url: "/pay/post",
dataType: "text",
data: {
user: $("input[name=user]").val(),
price: $("input[name=price]").val()
},
headers: {
"RequestVerificationToken": '@csrf.GetAndStoreTokens(Context).RequestToken'
},
type: "post",
success: function (res) {
alert(res);
}
});
})
})
</script>
将ajax请求头里加上token,这样服务端就会校验。
运行结果
配置
这个令牌验证还是需要借助cookie的
配置 在setup里,可以根据需要去添加,比如需要验证的域名
services.AddAntiforgery(options =>
{
options.Cookie = new CookieBuilder { Domain = "company.com" };
});