转载   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. live555源码研究(五)------DynamicRTSPServer类

    一.类DynamicRTSPServer作用 1,提供RTSP服务 二.类DynamicRTSPServer继承关系图

  2. js常用函数(不断添加中。。。)

    /************************************************* Function: getColor Description: 根据输入的数字返回一个颜色值 In ...

  3. ios开发--网页中调用JS与JS注入

    先将网页弄到iOS项目中: 网页内容如下, 仅供测试: <html> <head> <meta xmlns="http://www.w3.org/1999/xh ...

  4. Android:安卓资源引用符号的含义

    @代表引用资源 @*代表引用系统的非public资源,如: @*android:color/white @[package:]type/name引用自定义资源,如: android:text=&quo ...

  5. centos安装异常解决方法

    VMware系统安装Centos7后,第一次启动出现以下异常信息: Initial setup of CentOS Linux (core) ) [x] Creat user ) [!] Licens ...

  6. Armitage主屏幕说明与命令行启动

    (1)我们将Armitage主屏幕标注为A.B和C A:该区域显示预配置的模块.您可以在模块列表下面的文本框中输入要查找的模块进行查找. B:该区域显示我们可以进行漏洞测试的活跃主机. C:该区域显示 ...

  7. P125、面试题19:二叉树的镜像

    题目:请完成一个函数,输入一个二叉树,该函数输出它的镜像二叉树结点的定义如下:struct BinaryTreeNode{       int     m_nValue;       BinaryTr ...

  8. Linux内核3.0移植并基于Initramfs根文件系统启动

    Linux内核移植与启动 Target borad:FL2440 Bootloader:U-boot-2010.09 交叉编译器:buildroot-2012.08 1.linux内核基础知识 首先, ...

  9. 【HDOJ】4089 Activation

    1. 题目描述长度为n的等待队列,tomato处于第m个,有如下四种可能:(1)激活失败,概率为$p_1$,队列中的顺序不变:(2)连接失败,概率为$p_2$,队头玩家重新排在队尾:(3)激活成功,概 ...

  10. Android开发之onClick事件的实现

    算是从2015年开始学习android开发,目前把onClick的事件实现写下来,记录下,以备参考. 实现button的点击功能,让textView显示一行文字,最简单的onClick事件. 直接贴代 ...