Monthly Archives: February 2010

Verifying Anti-Forgery Token for Form POST only

As many of you know, the ASP.NET MVC Anti-Forgery token helps thwart Cross Site Request Forgery attacks. Any site that uses authenticated sessions (99% of web apps) should use similar mechanisms so these attacks cannot occur.

Very often, I would write GET and POST actions in the same method. This allows fall through to that same code we used for GET request if POST validation fails, ensuring consistency.

public ActionResult EditPerson(Person person)
{
	if (Request.HttpMethod == "POST" && ModelState.IsValid)
	{
		// do edit person...
		
		return RedirectToAction("Index");
	}

	// do get person

	return View(person);
}

If I use that sort of paradigm, then the [VerifyAntiForgeryToken] attribute would block both GET and POST requests when the token is not supplied. I want the token to be only verified when I POST. Since ASP.NET MVC is extensible, the normal way to go about that would be modify the behaviour of [VerifyAntiForgeryToken] by subclassing. Unfortuantely, VerifyAntiForgeryTokenAttribute is sealed which means it can’t be inherited from. Luckily, borrowing from the same trick that Http*Base classes use to combat sealed Http* classes, we can just create a new attribute that wraps the old attribute, implementing the same members and proxying the calls back to the wrapped base class. That’s exactly what I did and it works quite well. The result is [ValidateAntiForgeryTokenOnPost] which will only verify the form anti-forgery token on a POST request:
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
public class ValidateAntiForgeryTokenOnPostAttribute : FilterAttribute, IAuthorizationFilter
{
	private ValidateAntiForgeryTokenAttribute _wrapped;
	public ValidateAntiForgeryTokenOnPostAttribute()
	{
		_wrapped = new ValidateAntiForgeryTokenAttribute();
	}

	public string Salt { get { return _wrapped.Salt; } set { _wrapped.Salt = value; } }

	public void OnAuthorization(AuthorizationContext filterContext)
	{
		if (filterContext.HttpContext.Request.HttpMethod == "POST")
		{
			_wrapped.OnAuthorization(filterContext);
		}
	}
}

This will teach those sealed classes!