转载   http://kevintsengtw.blogspot.co.nz/2013/01/aspnet-mvc-validateantiforgerytoken.html

在 ASP.NET MVC 裡為了要防止 CSRF (Cross-Site Request Forgery) 跨站偽造請求的攻擊,我們可以在 View 的表單中加入「@Html.AntiForgeryToken」然後在對應的後端 Action 方法加上「ValidateAntiForgeryToken」Attribute 來防止 CSRF 的攻擊,相關

而當網站接收到沒有 AntiForgeryToken 的 POST 請求時就會返回錯誤訊息,如果沒有指定錯誤頁的話大多會是使用預設的 Error Page,比較好的錯誤訊息顯示方式是不要透漏太多訊息,以免讓有心者可以去鑽漏洞,但如果真的想要指定錯誤頁的話,也是有方法的,這篇文章當中就跟各位來說明。

什麼是 CSRF ( Cross-Site Request Forgery ) 跨站偽造請求呢?最常看到的就是經由網站裡的表單利用 POST 來發動攻擊,一般我們做網站都是預設站內的所有 POST 需求都是經由站內的表單或是 Javascript 程式所發出的,但有心攻擊的人就會利用這一點來對網站做壞事,例如我有一個簡單的登入頁面,

有心想做壞事的人是不會一直在這個頁面上去做踹站的動作,而是會自己寫個偽造的登入頁面然後跨站傳送 POST 請求到我們網站來踹,

這只是一個單純的 HTML 檔案,只是在表單的 Action 是使用測試網站的網址,

在這個偽造的網頁表單中輸入正確的帳號與密碼,就可以長驅直入網站當中,

所以不管我們在網站登入頁做的多麼嚴謹或是再多防護,都有可能因為這樣的疏忽而大開方便之門。

為了避免這樣的情況發生,我們可以在 View 頁面的表單中加入「@Html.AntiForgeryToken」,然後在對應的 Action 方法上標註「ValidateAntiForgeryToken」Attribute,如此就可以避免跨站偽造請求的攻擊,

在 View 頁面的表單裡加上「@Html.AntiForgeryToken」,產出頁面的原始碼會是以 Hidden 欄位放置 Token,

並且也會產生 Cookie「__RequestVerificationToken」來放置 Request Verification Token,

如果是經由偽造的頁面所傳送過來的 POST Request,那麼系統就會顯示錯誤,

在上面未加入 AntiForgeryToken 的情況下會返回一個錯誤頁,而這個錯誤頁不應該出現在一般使用者的眼前,因為這樣的錯誤訊息內容透露了很多系統內部細節,所以我在之前的文章也強調過應該要使用專屬的錯誤訊息頁來顯示,而錯誤訊息頁的內容也不要透露太多的細節,一般的情況下,我們都會在 Web.Config 的 System.Web Section 加入 customError 的內容,如下:

那麼當遇到 CSRF 傳送時就會返回系統預設的錯誤頁內容,

如果你想要讓 CSRF 所返回的網頁不是用預設的 Error.cshtml 的話,可以在 ~/View/Shared 目錄下建立一個新的錯誤頁,例如我另外建立一個「CSRF_Error.cshtml」

在 Controller 的 Action 方法上就必須要再去標註 HandleError Attribute,並在這個 Attribute 內指定 ExceptionType 以及返回的錯誤頁名稱,

後端沒有接收到前端 View 所 POST 回來的 AntiForgeryToken 時是會發生 HttpAntiForgeryException (MSDN : HttpAntiForgeryException 類別),所以在 HandleError 的要指定當發生 HttpAntiForgeryException 時所返回錯誤訊息所使用的 View 名稱,再一次重新傳送 CSRF POST 請求後,就會顯示指定的錯誤頁內容,

如果有安裝 elmah 來記錄系統錯誤的話,可以在 elmah Dashboard 裡看到的 HttpAntiForgeryException 錯誤,

而我在之前的文章裡曾經有介紹過在 Global.asax 的 Application_Error 事件中對系統發生錯誤時進行處理,並且依照錯誤的不同而顯示不同的錯誤頁面,

ASP.NET MVC + ELMAH 監控並記錄你的網站錯誤資訊 1

雖然 HttpAntiForgeryException 是 Http Statu Code 500 的 Internal Error,但並不會返回我們所指定的 InternalError 錯誤頁,所以我這邊的作法是自己再另外建立一個 HandleError Attribute,然後在引發 Exception 時去判斷 Exception 的類別,然後依照類別去做不同的處理,例如遇到 HttpAntiForgeryExceptoipn 這個類別的 Exception 就顯示指定的錯誤訊息頁,

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Web;
using System.Web.Http.Filters;
using System.Web.Mvc; namespace MvcApplication1.Base
{
public class CustomHandleErrorAttribute : System.Web.Mvc.FilterAttribute, System.Web.Mvc.IExceptionFilter
{
public void OnException(ExceptionContext filterContext)
{
if (!filterContext.IsChildAction
&& (!filterContext.ExceptionHandled && filterContext.HttpContext.IsCustomErrorEnabled))
{
var innerException = filterContext.Exception;
if ((new HttpException(null, innerException).GetHttpCode() == ))
{
var controllerName = (string)filterContext.RouteData.Values["controller"];
var actionName = (string)filterContext.RouteData.Values["action"];
var viewName = "Error"; if (typeof(HttpAntiForgeryException).IsInstanceOfType(innerException))
{
controllerName = "ErrorPage";
actionName = "AntiForgeryError";
viewName = "~/Views/ErrorPage/AntiForgeryError.cshtml";
}
else
{
controllerName = "ErrorPage";
actionName = "InternalError";
viewName = "~/Views/ErrorPage/InternalError.cshtml";
} var model = new HandleErrorInfo(filterContext.Exception, controllerName, actionName);
var result = new ViewResult
{
ViewName = viewName,
ViewData = new ViewDataDictionary<HandleErrorInfo>(model),
TempData = filterContext.Controller.TempData
}; filterContext.Result = result;
filterContext.ExceptionHandled = true;
filterContext.HttpContext.Response.Clear();
filterContext.HttpContext.Response.StatusCode = ;
filterContext.HttpContext.Response.TrySkipIisCustomErrors = true; var version = Assembly.GetExecutingAssembly().GetName().Version;
filterContext.Controller.ViewData["Version"] = version.ToString();
}
}
} }
}

其中的 viewName 需要指定絕對路徑,如果不把路徑名稱寫清楚,就只會在 ~/Views/Shared 目錄裡找,找不到就會引發 InvalidOperationException 的例外。

建立好 CustomHandleErrorAttribute 後,原本標註在 Login 方法上的 HandleError Attribute 就可以拿掉,而接著要到 ~/App_Start 目錄下的「FilterConfig.config」做修改,

把原本所使用的 HandleErrorAttribute 置換為我們所建立的 CustomHandleErrorAttribute, 修改如下,

using System.Web;
using System.Web.Mvc;
using MvcApplication1.Base; namespace MvcApplication1
{
public class FilterConfig
{
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new CustomHandleErrorAttribute());
//filters.Add(new HandleErrorAttribute());
}
}
}

修改完成後,重新建置方案並重新傳送 CSRF 請求,就可以看到我們所指定的錯誤內容,

雖說可以讓我們自己依據不同的錯誤或是例外來執行不同的處理或是顯示不同的錯誤訊息頁,但還是不要也不宜做得太細、太多,還是要避免過多的訊息暴露在外而讓人有機可趁。

參考連結:

The Code Project - Exception Handling in ASP.NET MVC  by After2050

StackOverflow  - HandleErrorAttribute not working

StackOverflow - ASP.net MVC - Custom HandleError Filter - Specify View based on Exception Type

延伸閱讀:

Steven Sanderson's blog : Prevent Cross-Site Request Forgery (CSRF) using ASP.NET MVC’s AntiForgeryToken() helper

隱匿角落 : 关于CSRF攻击及mvc中的解决方案 [ValidateAntiForgeryToken]

ASP.NET MVC中防止跨站请求攻击(CSRF)的更多相关文章

  1. asp.net MVC中防止跨站请求攻击(CSRF)的ajax用法

    参考: Preventing Cross-Site Request Forgery (CSRF) AttacksValidating .NET MVC 4 anti forgery tokens in ...

  2. ASP.NET MVC 实现 AJAX 跨域请求

    ASP.NET MVC 实现AJAX跨域请求的两种方法 和大家分享下Ajax 跨域的经验,之前也找了好多资料,但是都不行,后来看到个可行的修改了并测试下 果然OK了   希望对大家有所帮助! 通常发送 ...

  3. ASP.NET MVC 实现AJAX跨域请求方法《1》

    ASP.NET MVC 实现AJAX跨域请求的两种方法 通常发送AJAX请求都是在本域内完成的,也就是向本域内的某个URL发送请求,完成部分页面的刷新.但有的时候需要向其它域发送AJAX请求,完成数据 ...

  4. ASP.NET MVC中设置跨域

    ASP.NET MVC中设置跨域 1.什么是跨域请求 js禁止向不是当前域名的网站发起一次ajax请求,即使成功respone了数据,但是你的js仍然会报错.这是JS的同源策略限制,JS控制的并不是我 ...

  5. js基础 js自执行函数、调用递归函数、圆括号运算符、函数声明的提升 js 布尔值 ASP.NET MVC中设置跨域

    js基础 目录 javascript基础 ESMAScript数据类型 DOM JS常用方法 回到顶部 javascript基础 常说的js包括三个部分:dom(文档document).bom(浏览器 ...

  6. ASP.NET Core-Docs:在 ASP.NET Core 中启用跨域请求(CORS)

    ylbtech-ASP.NET Core-Docs:在 ASP.NET Core 中启用跨域请求(CORS) 1.返回顶部   2.返回顶部   3.返回顶部   4.返回顶部   5.返回顶部 1. ...

  7. 跨站请求伪造 CSRF / XSRF<一:介绍>

    跨站请求伪造(英语:Cross-site request forgery),也被称为 one-click attack 或者 session riding,通常缩写为 CSRF 或者 XSRF, 是一 ...

  8. PHP安全编程:跨站请求伪造CSRF的防御(转)

    跨站请求伪造(CSRF)是一种允许攻击者通过受害者发送任意HTTP请求的一类攻击方法.此处所指的受害者是一个不知情的同谋,所有的伪造请求都由他发起,而不是攻击者.这样,很你就很难确定哪些请求是属于跨站 ...

  9. 跨站请求伪造(CSRF)-简述

    跨站请求伪造(CSRF)-简述 跨站请求伪造(英语:Cross-site request forgery),也被称为 one-click attack 或者 session riding,通常缩写为 ...

随机推荐

  1. java内存模型 年轻代/年老代 持久区

    jvm中的年轻代 老年代 持久代 gc   虚拟机中的共划分为三个代:年轻代(Young Generation).老年代(Old Generation)和持久代(Permanent Generatio ...

  2. java使用redis

    1.redis和memecache这种缓存框架,都分为服务器端和客户端. 我们在项目中使用,相当于客户端,要引入客户端使用redis的jar包 首先你需要下载驱动包,下载 jedis.jar,确保下载 ...

  3. Android:属性scaleType与图片的显示

    ImageView是Android中的基础图片显示控件,该控件有个重要的属性是ScaleType,该属性用以表示显示图片的方式, 共有8种取值 matrix 用矩阵来绘制(从左上角起始的矩阵区域) f ...

  4. Android隐藏输入法键盘(hideSoftInputFromInputMethod没有效果)

    在个别时候,需要强制隐藏Android输入法键盘,如当前键盘正在显示,这个时候点击了侧滑面板,就要强制隐藏输入法键盘.网上常见的方法有: 1. InputMethodManager imm = (In ...

  5. MySQL复制应用中继日志解析

    前言:SQL线程应用中继日志,在binlog_format是row格式的时候,是居于主键更新,下面结合一张图来证明 1.从一个大神那边得到一张图片,SQL线程应用中继日志流程,下面就实验验证一下:(P ...

  6. 25.allegro中模块复用[原创]

    一,Module reuse 1,打开原理图 ------------------- --------------------- ctrl+i过滤器 直选part ctrl+e 查看属性 查看: 是否 ...

  7. 1523. K-inversions(K逆序对)

    1523 这题应该说有一些DP的思想吧 dp[i][j]表示以i为结尾第j个数的个数 k单调下降 直接求的话肯定超时 然后用树状数组来进行维护 求k-1次树状数组 #include <iostr ...

  8. 深入理解Android内存管理原理(六)

    一般来说,程序使用内存的方式遵循先向操作系统申请一块内存,使用内存,使用完毕之后释放内存归还给操作系统.然而在传统的C/C++等要求显式释放内存的编程语言中,记得在合适的时候释放内存是一个很有难度的工 ...

  9. Codeforces 364A - Matrix

    原题地址:http://codeforces.com/problemset/problem/364/A 题目大意: 给定一个数字a(0 ≤ a ≤ 109)和一个数列s(每个数都是一位,长度不超过40 ...

  10. sencha touch2 动画问题

    最近在review一个项目的代码, 发现返回操作比较乱,很多"从哪里来,到哪里去的操作"被写的一塌糊涂; 按照ios系统的进场出场动画(人家的体验还是很好的,必须借鉴)为标准,使用 ...