Daily Archives: February 7, 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.
[csharp]
public ActionResult EditPerson(Person person)
{
if (Request.HttpMethod == "POST" && ModelState.IsValid)
{
// do edit person…

return RedirectToAction("Index");
}

// do get person

return View(person);
}
[/csharp]
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:
[csharp]
[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);
}
}
}
[/csharp]
This will teach those sealed classes!