Click here to Skip to main content
15,884,628 members
Articles / DevOps
Tip/Trick

Validate Anti-Forgery in ASP.NET MVC- The Automated Way

Rate me:
Please Sign up or sign in to vote.
4.73/5 (7 votes)
13 Feb 2017CPOL 25K   364   8   2
Validate Anti-Forgery in 6 easy steps

Introduction

While trying to secure our ASP.NET MVC Web applications with recommended stuff like [ValidateAntiForgeryToken] filter to be protected from CSRF attack.

Reference: OWASP – Cross-Site Request Forgery (CSRF)

I found that when we apply the traditional way, it’s more likely to forget these stuff somewhere, if it’s not you, it will be your teammates.

Traditional Way

C#
// Action

[ValidateAntiForgeryToken]
public ActionResult AddUser(string userName)
{
    return View();
}
ASP.NET
// View
@using(Html.BeginForm("AddUser", "Home"))
{
    @Html.AntiForgeryToken()
    @Html.TextBox("userName")
    <button type="submit">Save</button>
}

So why we don’t we do this stuff automatically for all Post requests as recommended.

Automated Way

By applying these 6 easy steps, we will be able to protect our web applications from Cross-Site Request Forgery (CSRF).

Back End Work

  1. Security Filter Provider: This filter provider will apply ValidateAntiForgeryToken filter attribute on all Post requests:
    C#
    public class SecurityFilterProvider : IFilterProvider
    {
        public IEnumerable<Filter> GetFilters(ControllerContext controllerContext, 
                              ActionDescriptor actionDescriptor)
        {
            List<Filter> filterSet = new List<Filter>();
            // ValidateAntiForgeryToken for POST requests + 
            // Skip Actions with UnValidateAntiForgeryToken
            string verb = controllerContext.HttpContext.Request.HttpMethod;
    
            if (String.Equals(verb, "POST", StringComparison.OrdinalIgnoreCase) 
                && !actionDescriptor.IsDefined(typeof(UnValidateAntiForgeryToken), true))
            {
                filterSet.Add(new Filter(new ValidateAntiForgeryTokenAttribute(), 
                              FilterScope.Global, null));
            }
    
            return filterSet;
        }
    }
  2. Register Filter Provider in Global.asax:
    C#
    protected void Application_Start()
    {
       ...
       FilterProviders.Providers.Add(new SecurityFilterProvider());
       ...
    }
  3. This filter skips Action from being validated automatically:
    C#
    public class UnValidateAntiForgeryToken : ActionFilterAttribute
    {
        // This Filter has no use except skipping AntiForgeryToken Attribute from taking place
    }
    
    [HttpPost]
    [UnValidateAntiForgeryToken]
    public ActionResult AddUser(string userName)
    {
        ...
    }

Front End Work

  1. Render AntiForgeryToken input variable, this should be in Layout or View.
    ASP.NET
    var antoForgeryToken = '@Html.AntiForgeryToken()';
  2. This will post AntiForgeryToken with forms:
    JavaScript
    // Append AntiForgeryTiken Input to Form
    $('form').submit(function (event) {
        if ($(this).attr("method").toUpperCase() == "POST" 
        && !$(this).find("[name=" + $(antiForgeryToken).attr("name") + "]").length) {
            $(this).append($(antiForgeryToken));
        }
    });
  3. This will post AntiForgeryToken with Jquery-Ajax, and Ajax Action Links:
    JavaScript
    // Append AntiForgeryToken to Jquery Ajax Requests$.param()
    $.ajaxPrefilter(function (options, originalOptions, jqXHR) {
        if (options.type.toUpperCase() == "POST") {
            if (!originalOptions.data.__RequestVerificationToken) {
                var token = { __RequestVerificationToken: $(antiForgeryToken).val() };
                var data = $.isArray(originalOptions.data) ? 
                           originalOptions.data[0] : originalOptions.data;
                $.extend(data, token);
                options.data = $.param(data);
            }
        }
    });

Recommended

We can use @Html.BeginSecureForm instead of BeginForm, and @Ajax.BeginSecureForm.

This Overload methods adds AntiForgery Token to the form:

ASP.NET
// View
@using (Html.BeginSecureForm("AddUser", "Home"))
{
    @Html.TextBox("userName")
    <button type="submit">Save</button>
}

 

C#
// BeginForm replacement for HtmlHelper
public static MvcForm BeginSecureForm
(this HtmlHelper htmlHelper, string actionName, string controllerName)
{
    var form = htmlHelper.BeginForm(actionName, controllerName);
    htmlHelper.ViewContext.Writer.Write(htmlHelper.AntiForgeryToken().ToHtmlString());
    return form;
}

// BeginForm replacement for AjaxHelper
public static MvcForm BeginSecureForm(this AjaxHelper ajaxHelper, AjaxOptions ajaxOptions)
{
    var form = ajaxHelper.BeginForm(ajaxOptions);
    ajaxHelper.ViewContext.Writer.Write(AntiForgery.GetHtml());
    return form;
}

All overloading methods of BeginSecureForm is found at SecureFormExtensions.cs and SecureAjaxExtensions.cs.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior) Thiqah Business Services
Egypt Egypt
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
QuestionNot Working with JsonResult Pin
Member 1527059715-Jul-21 1:00
Member 1527059715-Jul-21 1:00 
QuestionWhere exactly its validating the Anti-Forgery token int he code? Pin
Abhijeet Shastry4-Apr-19 3:31
Abhijeet Shastry4-Apr-19 3:31 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.