1. 程式人生 > >ASP.NET MVC與CSRF(跨站腳本)攻擊

ASP.NET MVC與CSRF(跨站腳本)攻擊

轉移 off end gis 帳戶 blank 表單 密碼 message

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的

[email protected]()和在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是從何而來呢,[email protected]()方法隨機生成了一串明文,然後再對明文加密放在頁面和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站點作為解析。

ASP.NET MVC與CSRF(跨站腳本)攻擊