ASP.NET Core 防止跨站请求伪造(XSRF/CSRF)攻击

C#

浏览数:56

2019-9-17

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" };
            });

demo代码见 github|

作者:sands