Click here to Skip to main content
Click here to Skip to main content
Technical Blog

Tagged as

CSRF and AntiForgeryToken

, 15 Apr 2014 CPOL
Rate this:
Please Sign up or sign in to vote.
Create a custom authorization filter.

"THE ARTICLE IS NO LONGER MAINTAINED HERE. PLEASE POST ANY FURTHER DISCUSSIONS/QUESTIONS IN MY BLOG http://www.prideparrot.com/blog/archive/2012/7/securing_all_forms_using_antiforgerytoken" - Author

CSRF and AntiForgeryToken

Cross Site Request Forgery also known as CSRF (XSRF) is a widely exploited website vulnerability. In a CSRF attack, a malicious site instructs a victim's browser to send a request to an honest site, as if request were part of the victim's interaction with the honest site, leveraging the victim's network connectivity and the browser's state, such as cookies, to disrupt the integrity of the victim's session with the honest site. One of the popular technique to prevent CSRF attack is by using security tokens (from here).

ASP.NET MVC suports prevention against CSRF through the AntiForgeryToken HTML helper and ValidateAntiForgeryToken filter. The AntiForgeryToken is supported only for the POST requests and not for GET and this makes sense because the GET operation has to used only for safe operations (as per HTTP spec.).

In some applications we need all the POST operations should be validated for the anti-forgery token and in those cases instead of decorating all the POST actions in the application with the ValidateAntiForgeryTokenAttribute we can create a custom authorization filter and apply it globally, that's what we are going to see in this article. We will also see how to create an HTML helper that renders a form along with the hidden field that contains the security token.

What the AntiForgeryToken helper does?

The Html.AntiForgeryToken() creates a security token and sets it to a hidden field along with that it also sets the token to a httponly cookie.

@using(Html.BeginForm())
{
    <p>
        @@Html.AntiForgeryToken()
    </p>
}

Hidden field

<form ..>
...
<input type="hidden" 
    name="__RequestVerificationToken">
    value="d1Hh28W3uTpdZcEG0VhEkYg7D5XqFM9Sm4iA2e/cXgrOIIpPDENi1lVBg6mYLBAAoGk0q5RA/EPE2o6W5VAqd
         ziURtyqcdFrEcDmSID4vtOF+Nm2Zgf5EJRoRCTUzWvEgcffCvWgfATcznKjnZExjGcbMYQKLhkWBKydzzi4/UE="     
</form>

Cookie

__RequestVerificationToken_Lw__=+9UmEtsg2JLwoNO8eumR6fXWw/LnWU61oOiGvT+wNWPCrzfGSP++ODSJbUaeq7/7s
   DBjW8vQq3QRpEIRERWGcTFVQUUNVfFx5FM4P8oy0V6vPUNayyn8QM3vCdgYx/WwumyKauQWJb+Ysduni9T2rbKjRXJ6LqLbIcTX8CuOU2g=

What is the use of ValidateAntiForgeryToken(Attribute) filter?

The ValidateAntiForgeryToken filter checks whether the request contains the security token or not and if it doesn't contains the token then it generates the following error.

A required anti-forgery token was not supplied or was invalid.

[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Save(Model model)
{
    ...
}

One disadvantage is this can't apply this filter at the global level because it check all the requests(GET, POST) for the security token and our requirement is to check only for POST requests. The ValidateAntiForgeryTokenAttribute is a sealed class so we can't inherit and extend it instead we have to create a brand new filter that composes it.

Custom AntiForgeryToken filter

public class AntiForgeryAttribute: IAuthorizationFilter
{
    public void OnAuthorization(AuthorizationContext authorizationContext)
    {
        if (authorizationContext.RequestContext.HttpContext.Request.HttpMethod != "POST")
            return;

        new ValidateAntiForgeryTokenAttribute().OnAuthorization(authorizationContext);
    }
}

The implementation is quite simple! our custom filter checks for the POST requests and if yes then instantiates and calls the OnAuthorization method of the built-in ValidateAntiForgeryToken filter.

Now all we have to do is add our custom AntiForgeryAttribute as global filter.

filters.Add(new AntiForgeryAttribute());

Creating a secure html form

When we need all our HTML forms that needs to be secured this is what we do,

@using(Html.BeginForm())
{
    ...
    Html.AntiForgeryToken()
}

It would be nice if we have a HTML helper that creates a form with security token hidden field and set the cookie.

Here is a simple helper!

public static MvcForm BeginSecureForm(this HtmlHelper htmlHelper, 
string actionName, string controllerName)
{
    TagBuilder tagBuilder = new TagBuilder("form");

    tagBuilder.MergeAttribute("action", 
    UrlHelper.GenerateUrl(null, actionName, controllerName, new RouteValueDictionary(), 
    htmlHelper.RouteCollection, htmlHelper.ViewContext.RequestContext, true));
    tagBuilder.MergeAttribute("method", "POST", true);

    htmlHelper.ViewContext.Writer.Write(tagBuilder.ToString(TagRenderMode.StartTag));
    htmlHelper.ViewContext.Writer.Write(htmlHelper.AntiForgeryToken().ToHtmlString());
    var theForm = new MvcForm(htmlHelper.ViewContext);

    return theForm;
}

Now we can create a secure form simply like this:

@using(Html.SecureForm())
{
    ...
}

By passing AntiForgeryValidation for some actions

For some POST actions we may don't need the anti-forgery check and with the current implementation we can't achieve that. To satisfy this, all we have to do is create a simple attribute and decorate the actions that doesn't need security check with that.

public class NoAntiForgeryCheckAttribute: Attribute
{
}

[NoAntiForgeryCheck]
public ActionResult NotSecuredAction(Model model)
{
}

We have to update our AntiForgeryAttribute to take care of that NoAntiForgeryCheckAttribute as below:

public class AntiForgeryAttribute: IAuthorizationFilter
{
    public void OnAuthorization(AuthorizationContext authorizationContext)
    {
        if (authorizationContext.RequestContext.HttpContext.Request.HttpMethod != "POST")
            return;

        if (authorizationContext.ActionDescriptor.GetCustomAttributes(
                    typeof(NoAntiForgeryCheckAttribute), true).Length > 0)
            return;

        new ValidateAntiForgeryTokenAttribute().OnAuthorization(authorizationContext);
    }
}

Yeah! we are done. Now all our POST actions are safe against CSRF vulnerability, hope so Wink | ;)

License

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

Share

About the Author

After2050
Software Developer Trigent Software Private Limited
India India
I'm a software developer from south tip of India. I spent most of the time in learning new technologies. I've a keen interest in client-side technologies especially JavaScript and admire it is the most beautiful language ever seen.
 
I like sharing my knowledge and written some non-popular articles. I believe in quality and standards but blames myself for lagging them.
 
I believe in small things and they makes me happy!
Follow on   Twitter

Comments and Discussions

 
Questionmy vote of five... good kick start... Pinmembermanishpatel42014-Apr-14 13:54 
GeneralMy vote of 1 PinmemberNitin Kumar Chaudhary13-Sep-12 2:04 
GeneralRe: My vote of 1 PinmemberAfter205013-Sep-12 6:13 

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

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

| Advertise | Privacy | Mobile
Web02 | 2.8.141029.1 | Last Updated 15 Apr 2014
Article Copyright 2012 by After2050
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid