Click here to Skip to main content
Click here to Skip to main content

CSRF and AntiForgeryToken

By , 11 Jul 2012
 

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)

About the Author

After2050
Software Developer Trigent Software Private Limited
India India
Member
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!

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
GeneralMy vote of 1memberNitin Kumar Chaudhary13 Sep '12 - 2:04 
where is the code?
GeneralRe: My vote of 1memberAfter205013 Sep '12 - 6:13 
If you need the source code just ask it. You can't vote 1 because the article doesn't have source code (or is there any reason?). By the way I don't care much about the votes.

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

Permalink | Advertise | Privacy | Mobile
Web01 | 2.6.130523.1 | Last Updated 11 Jul 2012
Article Copyright 2012 by After2050
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid