Click here to Skip to main content
14,241,514 members

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

Rate this:
4.73 (7 votes)
Please Sign up or sign in to vote.
4.73 (7 votes)
13 Feb 2017CPOL
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

// Action

[ValidateAntiForgeryToken]
public ActionResult AddUser(string userName)
{
    return View();
}
// 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:
    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:
    protected void Application_Start()
    {
       ...
       FilterProviders.Providers.Add(new SecurityFilterProvider());
       ...
    }
  3. This filter skips Action from being validated automatically:
    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.
    var antoForgeryToken = '@Html.AntiForgeryToken()';
  2. This will post AntiForgeryToken with forms:
    // 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:
    // 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:

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

 

// 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)

Share

About the Author

Muhammad Albedewy
Software Developer (Senior) Thiqah Business Services
Egypt Egypt
No Biography provided

Comments and Discussions

 
QuestionWhere exactly its validating the Anti-Forgery token int he code? Pin
Abhijeet Shastry4-Apr-19 3:31
memberAbhijeet Shastry4-Apr-19 3:31 
Where exactly its validating the Anti-Forgery token int he code? How the code will react if validation failed? I saw other articles where the code was like below
public override void OnActionExecuting(System.Web.Mvc.ActionExecutingContext actionContext)
    {
        try
        {
            if ((actionContext.HttpContext.Request.IsAjaxRequest()))
            {
                ValidateRequestHeader(actionContext.HttpContext.Request);
            }

        }
        catch (System.Web.Mvc.HttpAntiForgeryException)
        {
            throw new HttpAntiForgeryException("Anti forgery token cookie not found");
        }
    }


But i can't find where its validating.

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.

Tip/Trick
Posted 12 Feb 2017

Stats

13.2K views
220 downloads
7 bookmarked