I am creating a custom AuthorizeAttribute for my JsonResult controllers to ensure that all requests are coming from an authorized user.
A token is created using @Html.AntiForgeryToken(), which is then added to the headers of the AJAX request.
The contoller method on the server has a [ValidateJsonAntiForgeryToken], which uses checks the token with AntiForgery.Validate() and returns a JSON formmatted error message if the toke is not valid.
When testing everything seemed to be fine until I tried just pasting in a value for the token from a diffent form, and it was accepted.
These are the things that did work as expected:
- Navigating directy to the URL (which did work before setting up the AuthorizeAttribute) - returned the error message
- No Token - returned the error message
- Token from other ASP. NET for (the "edit profile" form for this site) - returned the error message
- Token from @Html.AntiForgeryToken() in the view - returned the correct result from the controller
Things that did NOT work as expected
- Token from a different form on the same site, in the same session.
Should AntiForgery.Validate() be rejecting tokens from other forms in the same session?
Javascript in the view:
var headers = {}; //Gets the token from @Html.AntiForgeryToken() headers["__RequestVerificationToken"] = $('[name=__RequestVerificationToken]').val(); //Token from a different form I pasted here headers["__RequestVerificationToken"] = "2hwuX7Untp9r5lNqQF1RG8iWgwmLr4wVobB6foTdXvQdXCQrJUp6zsM3cTxVQ3bQjAWlEYYGOSxa5EcXl70WeCXlkipw6_00tyqUFjDSzziR0K1muy29j0vgOPHNqxR0"; $.ajax({ type: "POST", url: URL, dataType: "json", headers: headers, ...
ValidateJsonAntiForgeryToken
public class ValidateJsonAntiForgeryToken : AuthorizeAttribute { public JsonResult deniedResult = new JsonResult() { JsonRequestBehavior = JsonRequestBehavior.AllowGet, Data = new { StatusCode = HttpStatusCode.Forbidden, Error = "Access Denied" } }; public override void OnAuthorization(AuthorizationContext filterContext) { var request = filterContext.HttpContext.Request; if (request.HttpMethod == WebRequestMethods.Http.Post && request.IsAjaxRequest()) { //Accepts the pasted token AntiForgery.Validate(CookieValue(request), request.Headers["__RequestVerificationToken"]); } else { filterContext.Result = deniedResult; } } private string CookieValue(HttpRequestBase request) { var cookie = request.Cookies[AntiForgeryConfig.CookieName]; return cookie != null ? cookie.Value : null; } //Never gets called, even when there is no token protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext) { System.Diagnostics.Debug.WriteLine("ValidateJsonAntiForgeryToken HandleUnauthorizedRequest "); filterContext.Result = deniedResult; } }
Controller:
[ValidateJsonAntiForgeryToken] public JsonResult GetStuff() { }
Securely posting application/json data to Asp.Net MVC4
ASP.NET MVC Ajax CSRF Protection With jQuery 1.5