【.Net MVC】Ajax 使用 AntiForgeryToken

.net MVC Razor語法有個@Html.AntiForgeryToken(),來防止CSRF(Cross-Site Request Forgery)攻擊。

原本認為透過ajax的方法,就不用刻意防護了!

但,今天來紀錄ajax怎麼也可以使用AntiForgeryToken。

跨站請求偽造英語Cross-site request forgery),也被稱為 one-click attack 或者 session riding,通常縮寫為 CSRF 或者 XSRF, 是一種挾制用戶在當前已登錄的Web應用程式上執行非本意的操作的攻擊方法。[1]跨網站指令碼(XSS)相比,XSS 利用的是用戶對指定網站的信任,CSRF 利用的是網站對用戶網頁瀏覽器的信任。(資料來源:維基百科

 

在.net MVC的框架下,微軟已經設計出一個防範的方法。

Action透過ValidateAntiForgeryToken 這個Attribute,搭判Razor的Form 中使用@Html.AntiForgeryToken()。

如此,就足以防護CSRF攻擊。

 

網路上有位作者「Richard Gibson」,在2013年發表了一篇Blog。

Validating .NET MVC 4 anti forgery tokens in ajax requests

文中,提出兩個觀點:

  • 要使用這樣的方法,必須自行在需要使用的Action上套上ActionFilter,這件事情勢容易遺忘的。
  • 對於透過ajax方法要求的post,並不包含該驗證Token。

而也,提出了對應的方法(程式碼請前往該文章)。

  • 建立自訂的ActionFilter
  • Overrider OnAuthorization
    • 只判斷HttpPost
    • 判斷是否Request是否來自Ajax
    • 判斷Request的Header內的Token
  • 需要的Action上套用此自訂的ActionFilter(我直接在BaseController上,一勞永逸)
  • Client 端Ajax的時候,將Token加入到Header中。

Client端,因為只要忘記打也是枉費我們寫上面那一大段。

如果你使用jQuery。

可以透過在_Layout中,透過jQuery.ajaxSetup()jQuery.ajaxPrefilter() (1.5版以後)

jQuery官方建議不要使用ajaxSetup的方法。

Note: The settings specified here will affect all calls to $.ajax or Ajax-based derivatives such as $.get(). This can cause undesirable behavior since other callers (for example, plugins) may be expecting the normal default settings. For that reason we strongly recommend against using this API. Instead, set the options explicitly in the call or define a simple plugin to do so. (擷取自jQuery官網)

因為使用後,所有透過$.ajax()的方法,或是以Ajax為基礎的方法,如$.get()都會被受到影響。

如果有其他第三方服務若使用也會被受到影響,除非重寫該選項或是$.ajaxSetup()。

相較於$.ajaxSetup,$.ajaxPrefilter相對的提供了彈性。不過,上述的特性也一樣存在。

但,我們可以降低到比較小的影響範圍。

以本篇文章的案例,我在_Layout中寫成以下程式:

        $.ajaxPrefilter(function (options) {
            if (!options.beforeSend && options.type === 'Post') {
                options.beforeSend = function (xhr) {
                    xhr.setRequestHeader("__RequestVerificationToken", $('[name=__RequestVerificationToken]').val());
                }
            }
        });

只針對 Post來處理,而且若沒有設定過beforeSend才套用。

如此,正常情況下,不需在修改Script,即可達成目的。