Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

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

0.00/5 (No votes)
13 Feb 2017 1  
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 has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here