1. 程式人生 > >ASP.NET MVC防範CSRF最佳實踐

ASP.NET MVC防範CSRF最佳實踐

XSS與CSRF

哈哈,有點標題黨,但我保證這篇文章跟別的不太一樣。

我認為,網站安全的基礎有三塊:

  • 防範中間人攻擊
  • 防範XSS
  • 防範CSRF

注意,我講的是基礎,如果更高階點的話可以考慮防範機器人刷單,再高階點就防範DDoS攻擊,不過我們還是回到“基礎”這個話題上吧,對於中間人攻擊,使用HTTPS是正確且唯一的做法,其它都是歪門邪道,最好還要購買各個瀏覽器都承認的SSL證書;防範XSS,關鍵點在於將使用者提交資料呈現在頁面上的時候,需要使用Html Encode,或在處理帶HTML格式的使用者表單資料時,進行“消毒”(Sanitize)處理,關於這個,我前一篇文章《讓ASP.NET接受有“潛在危險”的提交

》已經講述了應該怎麼做;剩下了這個CSRF是本文要講的,我認為防範CSRF的前提是必須先做好XSS的防範工作,因為:CSRF防的是別的網站,如果自己的網站本身有XSS漏洞,被別人注入了有害指令碼,那麼就變成了“家賊難防”了。

至於什麼是CSRF,如何讓ASP.NET防範CSRF,這種文章很多的,比如部落格園裡這篇就可以:《ASP.NET MVC 防止 CSRF 的方法》,我總結一下一般的做法,也就這兩點:

  • 在cshtml頁面的form標籤里加上@Html.AntiForgeryToken()
  • 在Controller需要防範CSRF的Action上加上[ValidateAntiForgeryToken]註解

Done!

原理

byte[] plainText = Encoding.UTF8.GetBytes("123456");
string[] purposes = { "blah blah blah" };
byte[] cypherText = MachineKey.Protect(plainText, purposes);
Console.WriteLine(Convert.ToBase64String(cypherText));
plainText = MachineKey.Unprotect(cypherText, purposes);
Console.WriteLine(Encoding.UTF8.GetString(plainText));
  • 頁面上加上一個像這樣的標籤<input name="__RequestVerificationToken" type="hidden" value="(一長串密文)" />

接下來輪到ValidateAntiForgeryToken過濾器,它收到了請求,就嘗試解出請求中的Cookie的“一長串密文”和請求中的Form的“一長串密文”,解密後比對兩者的“隨機明文”,如果一致,則通過,(其實作為高階用法你還可以自定義一些額外的規則,不過這不在本文講述範圍內)否則丟擲HttpAntiForgeryException異常。

特點與侷限性

知道了原理,就來分析下它的特點與侷限性,首先很容易想到的就是:

  • 瀏覽器必須要支援Cookie
  • 只能驗證POST請求(因為需要Form)

另外思考一下如果一個頁面中呼叫了多次@Html.AntiForgeryToken(),生成了多個input標籤,會怎樣呢?會不會生成了兩個不同的Token,最後比對出錯?其實不必擔心,正兒八經的那個隨機明文只會生成一次,Html.AntiForgeryToken()方法會檢查你提交的Cookie,如果已存在__RequestVerificationToken,那麼它就不會再生成一個新的隨機數明文了。否則如果每次都生成一個隨機數明文,你的頁面上如果有兩個Form的話,其中一個肯定沒法正常提交,更不用說AJAX提交的情況。

AJAX POST

如果不是直接提交頁面上的表單,而是AJAX POST,像這樣:

$.ajax({
    type: "post",
    url: "/testurl",
    data: {test:'abc'},
    success: function (data) {
        //done!
    }
});

這可咋辦?你必須想方設法在data中帶上正確的__RequestVerificationToken啊!StackOverflow上有個解決方案,挺不錯,大家參考下:Go to StackOverflow,簡單地說就是寫一個ASP.NET MVC的HTML生成幫助方法,用於生成那“一長串密文”,交給這個ajax的data。

但這可不是我想說的“最佳實踐” ,再考慮一種情況:用js動態生成Form,然後Submit。嗯,我承認這個有點奇葩,但在我的專案中確實有不少地方是這麼幹的,你別問為什麼了,反正就是有,遇到這種情況,咋辦?看來還是得求助於js。下面我分享下我的做法:

我的“最佳實踐”

首先我沒有把@Html.AntiForgeryToken()放到每一個Form中,我只在一處地方用到了@Html.AntiForgeryToken(),那就是母版頁!接下來把下面這段js放到common.js中(common.js是母版頁引用的js,也就是說每個頁面會引用到):

    //處理form的submit事件,新增AntiForgeryToken到表單裡
    $("body").on("submit", "form", function () {
        var theForm = $(this);
        if (theForm.find("input[name='__RequestVerificationToken']").length === 0) {
            var antiForgery = $("input[name='__RequestVerificationToken']:first").val();
            if (antiForgery) {
                var theAntiForgeryTokenInput = $('<input />').attr('type', 'hidden')
                    .attr('name', '__RequestVerificationToken')
                    .attr('value', antiForgery);
                $(this).prepend(theAntiForgeryTokenInput);
            }
        }
    });

這樣一來,所有的form的submit動作就會在這裡被處理一下,新增上了__RequestVerificationToken這個欄位。接下來是AJAX POST的處理:

    data= {test:'abc'};

    var antiForgery = $("input[name='__RequestVerificationToken']:first").val();
    if (antiForgery) {
        if (!data.__RequestVerificationToken) {
            data.__RequestVerificationToken = antiForgery;
        }
    }

    $.ajax({
        type: "post",
        url: "/testurl",
        data: data,
        success: function (data) {
            //done!
        }
    });

嗯?你也許要問,每個用到AJAX POST的地方都加上這麼一段程式碼豈不是很繁瑣?是的,但在我的專案中,我用了幾個公共的方法對AJAX POST進行了一些封裝,所以只需要改好這幾個地方即可,你可以根據自己的專案的實際情況進行優化處理。

還有一種情況是用AJAX來提交Form,而不是像上面這樣的data:

    var form = $("#the-form-id");
    var dataToSubmit = form.serializeArray();
    var antiForgery = $("input[name='__RequestVerificationToken']:first").val();
    if (antiForgery) {
        var found = false;
        for (var i = 0; i < dataToSubmit.length; i++) {
            if (dataToSubmit[i].name === '__RequestVerificationToken') {
                found = true;
                break;
            }
        }
        if (!found) {
            dataToSubmit.push({ name: "__RequestVerificationToken", value: antiForgery });
        }
    }

    $.ajax({
        type: method,
        url: urlToSubmit,
        data: dataToSubmit,
        success: function (data) {
            //done!
        }
    });

照舊,根據你的專案的實際情況封裝一下,其實真正要改動的地方不多,只要你框架搭好了。

總結一下,框架搭好了的前提下,為了防範CSRF,你所需要做的事情就僅剩下:給帶[HttpPost]註解的Action新增[ValidateAntiForgeryToken]。

至於驗證失敗丟擲HttpAntiForgeryException異常導致預設錯誤頁面(我又叫它“死黃頁”,該死的黃頁的意思)出現的問題,你可以在Application_Error中處理一下啊,Google關鍵字“Application_Error”,一搜一大堆,或者,等我有空了再寫一篇這個主題的“最佳實踐”?

相關推薦

ASP.NET MVC防範CSRF最佳實踐

XSS與CSRF 哈哈,有點標題黨,但我保證這篇文章跟別的不太一樣。 我認為,網站安全的基礎有三塊: 防範中間人攻擊 防範XSS 防範CSRF 注意,我講的是基礎,如果更高階點的話可以考慮防範機器人刷單,再高階點就防範DDoS攻擊,不過我們還是回到“基礎”這個話題上吧,對於中間人攻擊,使用

ASP.NET MVCCSRF(跨站腳本)攻擊

轉移 off end gis 帳戶 blank 表單 密碼 message CSRF 一 何為CSRF CSRF(Cross-site request forgery跨站請求偽造,也被稱成為“one click attack”或者session riding,通常縮寫為CS

ASP.NET MVC 防止 CSRF 的方法

MVC中的Html.AntiForgeryToken()是用來防止跨站請求偽造(CSRF:Cross-site request forgery)攻擊的一個措施,它跟XSS(XSS又叫CSS:Cross-Site-Script),攻擊不同,XSS一般是利用站內信任的使用者在網站內插入惡意的指令碼程式碼進行攻擊,

ASP.NET Core Web API 最佳實踐指南

原文地址: ASP.NET-Core-Web-API-Best-Practices-Guide 介紹 當我們編寫一個專案的時候,我們的主要目標是使它能如期執行,並儘可能地滿足所有使用者需求。 但是,你難道不認為建立一個能正常工作的專案還不夠嗎?同時這個專案不應該也是可維護和可讀的嗎? 事實證明,我們需

ASP.NET Core 依賴注入最佳實踐與技巧[譯]

# ASP.NET Core 依賴注入最佳實踐與技巧 > 原文地址:https://medium.com/volosoft/asp-net-core-dependency-injection-best-practices-tips-tricks-c6e9c67f9d96 [正(ke)確(xue)上(shan

AOP實踐--ASP.NET MVC 5使用Filter過濾Action引數防止sql注入,讓你程式碼安全簡潔

在開發程式的過程中,稍微不注意就會隱含有sql注入的危險。今天我就來說下,ASP.NET mvc 5使用Filter過濾Action引數防止sql注入,讓你程式碼安全簡潔。不用每下地方對引數的值都進行檢查,看是使用者輸入的內容是否有危險的sql。如果沒個地方都要加有幾個缺

Asp.net MVC 如何防止CSRF攻擊

什麼是CSRF攻擊? CSRF(Cross-site request forgery跨站請求偽造,也被稱成為“one click attack”或者session riding,通常縮寫為CSRF或者XSRF,是一種對網站的惡意利用。儘管聽起來像跨站指令碼(XSS),但

什麼是CSRF攻擊,如何在ASP.NET MVC網站中阻止這種攻擊?

1   什麼是CSRF,如何防治? Cross-site request forgery 1.1 例子 假如一家銀行用以執行轉賬操作的URL地址如下: http://www.examplebank.com/withdraw?account=AccoutName&

IOC Of Ninject Base On ASP.NET MVC

dex 準備工作 應用 new 引用 cti err art part 說在之前的話 IOC的概念相信大家比較熟悉了,習慣性稱之為依賴註入或控制反轉,園子裏對基於MVC平臺IOC設計模式已經相當多了,但大家都只知道應該怎麽應用一個IOC模式,比如Ninject, Unity

ASP.Net MVC連接MySQL和Code First的使用

config文件 onf tsql .com mysql字符串 spa set web 不同 首先要準備一下的工具作為環境 MySQL Community Server 5.7.x My Workbench 6.3 VS2017 新建一個項目,NetMySQLCodeF

ASP.NET MVC 替換默認的基架模板

不想 esp black 函數返回 als work 相同 bae detail 在學習Asp.net Mvc中,今天第一次聽了基架,哈哈! 常用的首字母縮略詞 CRUD 恰當地傳達了根據數據存儲編寫例程創建、檢索、更新和刪除操作的普通任務。Microsoft 提供由

asp.net mvc 註冊中的郵箱激活功能實現

名稱 work 點擊 rom urn 內容 string 電子郵件 amp 基本流程圖 註冊頁面就不再寫出,現在將發送郵件的代碼粘貼出來 public ActionResult SendEmial() { int

ASP.NET MVC 5 Controllers and Actions

del png asp.net obj into sse ascx stat wan Creating a Controller with IController All controller classes must implemet IController interf

Asp.net MVC中如何獲取控制器的名稱

tex 如何 route 控制 this .get data control str 如果在代碼中 當前controller、action的獲取RouteData.Route.GetRouteData(this.HttpContext).Values["controlle

ASP.NET MVC 提高運行速度的幾種性能優化方法

服務 排隊 等待 谷歌搜索 部署 lan str run 提高 主要介紹ASP.NETMVC 應用提速的六種方法,因為沒有人喜歡等待,所以介紹幾種常用的優化方法。 大家可能會遇到排隊等待,遇到紅燈要等待,開個網頁要等待,等等等。 理所當然,沒有人喜歡等待網頁慢吞吞地加載

asp.net mvc中html helper的一大優勢

ont size color 優勢 其中 系統 per .net strong 剛上手這個框架,發現其中的html helper用起來很方便,讓我們這些從web form 過渡來的coder有一種使用控件的快感,嘻嘻! 言歸正傳,我要說的是在使用它時,系統會自動執行表單的現

asp.net mvc cookie超時返回登錄頁面問題

vid sig ror ttr resp 執行 bsp filter current filterContext.HttpContext.Response.Write("<script>top.location.href = ‘/Login/Index‘;<

淺析Asp.net MVC 中Ajax的使用

x11 生成 table ex18 review arp javascrip tle func 在ASP.NET MVC beta中我們可以使用Ajax.BeginForm, Ajax.ActionLink來進行Ajax調用,同樣我們也可以使用一些支持Ajax 框架如jQ

Asp.net MVC使用FormsAuthentication,MVC和WEB API可以共享身份認證 (轉載)

mlp ges web api nbsp 快速 charset 生成頁面 核心 lds 在實際的項目應用中,很多時候都需要保證數據的安全和可靠,如何來保證數據的安全呢?做法有很多,最常見的就是進行身份驗證。驗證通過,根據驗證過的身份給與對應訪問權限。同在Web Api中如何

不使用Visual Studio開發ASP.NET MVC應用(下篇)

構建 onf log rdquo 你們 err ica ide lan 書接上回! 前文“純手工”、徹底拋棄Visual Studio,制作了一個ASP.NET MVC應用,運行起來還不錯,項目目錄、源代碼、web.config等所有東西都已經做到