ASP.NET MVC与CSRF(跨站脚本)攻击

2018-06-22 06:14:36来源:未知 阅读 ()

新老客户大回馈,云服务器低至5折

CSRF

一 何为CSRF

CSRF(Cross-site request forgery跨站请求伪造,也被称成为“one click attack”或者session riding,通常缩写为CSRF或者XSRF,是一种对网站的恶意利用。需要注意的是,CSRF与XSS的区别,CSRF是其他网站进行对你的网站的攻击。

关于CSRF的详细信息请看:https://baike.baidu.com/item/CSRF/2735433

 

二 CSRF的危害

对CSRF进行简单了解后,我们先来看看CSRF攻击受害者需要几步。

受害者必须依次完成两个步骤:

  1.登录受信任网站A,并在本地生成Cookie。

  2.在不登出A的情况下,访问危险网站B。

此时危险网站B拥有受害者在信任网站A的登录验证cookie,假设Cookie没有失效或者过期,那么危险网站B就可以发起假冒的请求,来获取受害者在信任网站A的信息或者在受害者不知情的情况下,进行资金转移等。

 

三 MVC是如何防止CSRF的

MVC框架主要通过在form内添加@Html.AntiForgeryToken()和在action上添加 [ValidateAntiForgeryToken]进行防止。

具体代码如下:

1. 在cshtml页面加上 @Html.AntiForgeryToken()

<section id="loginForm">
@using (Html.BeginForm("Login", "Account", new { ReturnUrl = ViewBag.ReturnUrl }, FormMethod.Post, new { @class = "form-horizontal", role = "form" }))
{
@Html.AntiForgeryToken()
<h4>使用本地帐户登录。</h4>
<hr />
@Html.ValidationSummary(true, "", new { @class = "text-danger" })
<div class="form-group">
@Html.LabelFor(m => m.Email, new { @class = "col-md-2 control-label" })
<div class="col-md-10">
@Html.TextBoxFor(m => m.Email, new { @class = "form-control" })
@Html.ValidationMessageFor(m => m.Email, "", new { @class = "text-danger" })
</div>
</div>
<div class="form-group">
@Html.LabelFor(m => m.Password, new { @class = "col-md-2 control-label" })
<div class="col-md-10">
@Html.PasswordFor(m => m.Password, new { @class = "form-control" })
@Html.ValidationMessageFor(m => m.Password, "", new { @class = "text-danger" })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<div class="checkbox">
@Html.CheckBoxFor(m => m.RememberMe)
@Html.LabelFor(m => m.RememberMe)
</div>
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="登录" class="btn btn-default" />
</div>
</div>
<p>
@Html.ActionLink("注册为新用户", "Register")
</p>
@* 为密码重置功能启用帐户确认后,请启用此项一次
<p>
@Html.ActionLink("Forgot your password?", "ForgotPassword")
</p>*@
}
</section>

2. 在相应的action方法上添加[ValidateAntiForgeryToken]

 

//
// POST: /Account/Login
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Login(LoginViewModel model, string returnUrl)
{
if (ModelState.IsValid)
{
var user = await UserManager.FindAsync(model.Email, model.Password);
if (user != null)
{
await SignInAsync(user, model.RememberMe);
return RedirectToLocal(returnUrl);
}
else
{
ModelState.AddModelError("", "用户名或密码无效。");
}
}

// 如果我们进行到这一步时某个地方出错,则重新显示表单
return View(model);
}

 

3.MVC在预防CSRF上的原理

 @Html.AntiForgeryToken()方法会在浏览器上做两件事:

  1. 页面上加上一个标签<input name="__RequestVerificationToken" type="hidden" value="密文A" />

  2. 在浏览器上生成一个名为__RequestVerificationToken的Cookie,值为“密文B”

form表单提交时,会将页面上的密文A和浏览器的密文B一起提交给服务器端,服务器端分别对密文A和密文B进行解密,比对密文A和密文B解密后的明文字符串是否相同,如果相同,则验证通过。

那么密文A和密文B是从何而来呢,其实是上面的@Html.AntiForgeryToken()方法随机生成了一串明文,然后再对明文加密放在页面和cookie内,但是加密出来的密文不同。密文A每次刷新都会更新成不同的密文,但是一个浏览器进程内,COOKIE的密文好像不变(自己在firefox内试了几次,有兴趣的同学可以自己尝试一下)

四 AJAX请求如何防止CSRF

上面说了MVC框架如何防止CSRF的,但是只限于FORM表单提交,那么问题来了,在一般ajax请求时,没有form表单提交,这个时候该如何防止CSRF呢?网络上有很多不错的答案。我在写该篇随笔的时候也借鉴了很多前辈的方法。

下面介绍我的方法:

 

1. 在全局共享页面,添加密文生成代码:

@using (Html.BeginForm(null, null, FormMethod.Post, new { id = "__AjaxAntiForgeryForm" }))
{
@Html.AntiForgeryToken()
}

2. 收紧ajax请求方法入口,写扩展ajax方法避免重复工作,一定要注意黄色标记

$.extend({
  z_ajax: function (request) {
      var form = $('#__AjaxAntiForgeryForm');
      var antiForgery = $("input[name='__RequestVerificationToken']",form).val();
      var data = $.extend({ __RequestVerificationToken: antiForgery }, request.data);
      request = $.extend({
      type: "POST",
      dataType: "json",
      contentType: 'application/x-www-form-urlencoded; charset=utf-8',
      }, request);
      request.data = data;

  $.ajax(request);
}

3. 在需要的POST请求上,添加[ValidateAntiForgeryToken]

[HttpPost]
[ValidateAntiForgeryToken]
public JsonResult Test(string testString)
{
  var trustedString = Encoder.HtmlEncode(testString);
  return Json(trustedString);
}

4. 实现具体的ajax请求,该请求会自动将密文带到服务端,由服务端的特性验证

$(function () {
  $("#test").click(function ()
  {
    $.z_ajax(
    {
      url: "/Home/Test",
      data: {testString:'333333'},
      error: function (request, textStatus, errorThrown) {
        console.log(request, textStatus, errorThrown);
      },
      success: function (response)
      {
        alert(123);
      }
    });
  })
})

 

经过以上的讲解,大家应该对MVC 防止CSRF有了一定的认识。

正如上面所说,在编写这篇随笔的时候,参考了很多前辈的思路和结晶。在这里就不一一列举了,如果有什么问题,欢迎大家随时反馈。

以上案例使用VS2013自动生成的MVC5站点作为解析。

 

标签:

版权申明:本站文章部分自网络,如有侵权,请联系:west999com@outlook.com
特别注意:本站所有转载文章言论不代表本站观点,本站所提供的摄影照片,插画,设计作品,如需使用,请与原作者联系,版权归原作者所有

上一篇:asp.net mvc 动态编译生成Controller

下一篇:MVC4 中使用 Area 和 注意的地方