Click here to Skip to main content
13,894,773 members
Click here to Skip to main content
Add your own
alternative version

Stats

25.6K views
583 downloads
12 bookmarked
Posted 23 Feb 2015
Licenced CPOL

MVC5 Anti-forgery validator for HTTP Headers

, 23 Feb 2015
Rate this:
Please Sign up or sign in to vote.
ASP MVC5 authorization filter to validate anti-forgery token for JSON type of requests.

Introduction

ASP.NET MVC provides a simple and effective means to stop cross-site-request-forgery attack. However this scheme is available only for form data submissions. The purpose of this article is to create an authorization attribute which protects against cross-site request forgery for JSON like requests.

Background

ASP.NET MVC provides a simple and effective means to stop cross-site-request-forgery attack. Briefly, it works like this: when the server expects a user to send some information to it, it pass on a bit of a special bit of information to the client (browser). At this point this security information is known to both the genuine client  and the web server.

When the genuine client (browser) sends the user's information, it also sends this special bit of information. The server compares the two, if they match it means that the client sending the information is the original one and the request is genuine.

If a request is somehow sent to the server from another source this special bit of information will not be present which is then detected by the server and further request processing halts preventing the attack.

Anti-forgery in HTML forms

  1. First identify the controller method(s) you wish to protect. Apply the [ValidateAntiForgeryToken] attribute on them.
  2. Create the anti-forgery token in the form which is to be submitted to the server using the @Html.AntiForgeryToken(); helper. This creates a hidden form field whose name is ' __RequestVerificationToken' and value is the anti-forgery token. It also stores a corresponding token to compare against in the cookies collection named '__ __RequestVerificationToken'.

The ValidateAntiForgeryToken is an authorization filter (implements IAuthrizationFilter) and runs before the action method runs. If the request does not contain the necessary anti-forgery token, an exception is thrown and the action method will not execute.

Using the code

Arriving at the main purpose  of this post which to create such a scheme for non HTML form data - say you wish to protect a method which accepts a JSON string and no form elements. One option is to pass an anti-forgery token as a member of the JSON object itself and check this value manually but this is not reusable.

Another method could be to pass the anti-forgery token in the HTTP header and do something similar that ValidateAntiForgeryToken does. We'll consider this solution.

First we create a filter attribute named ValidateAntiForgeryHeader which checks the anti-forgery token but reads the token from the HTTP header:

public class ValidateAntiForgeryHeader : FilterAttribute, IAuthorizationFilter
{
	public void OnAuthorization(AuthorizationContext filterContext)
	{
		string clientToken = filterContext.RequestContext.HttpContext.Request.Headers.Get(KEY_NAME);
		if (clientToken == null)
		{
			throw new HttpAntiForgeryException(string.Format("Header does not contain {0}", KEY_NAME));
		}

		string serverToken = filterContext.HttpContext.Request.Cookies.Get(KEY_NAME).Value;
		if (serverToken == null)
		{
			throw new HttpAntiForgeryException(string.Format("Cookies does not contain {0}", KEY_NAME));
		}

		System.Web.Helpers.AntiForgery.Validate(serverToken, clientToken);
	}

	private const string KEY_NAME = "__RequestVerificationToken";
}

To perform the actual comparison of the anti-forgery token we make use of AntiForgery class present in System.Web.Helpers namespace.

We can now apply this attribute just like the ValidateAntiForgeryToken on a method we want to protect:

 

[ValidateAntiForgeryHeader]
public JsonResult SendMessage(MyModel model)
{
	MyModel m = new MyModel { Sender = "System", Message = "Thanks for your message " + model.Sender };
	return Json(m);
}

 

Now for the client side

We still use the @Html.AntiForgeryToken(); html helper to generate the hidden field with the token value.  Let's create a simple form which does two things:

  • Make a normal form submit if one click the "Submit Form" button
  • Makes a JSON request asynchronously if one clicks the "Send JSON" button
@{
    ViewBag.Title = "Test";
}

<h2>Test</h2>

@{
	IDictionary<string, object> attrs = new Dictionary<string, object>();
	attrs.Add("name", "TestForm");
	attrs.Add("id", "TestForm");
	attrs.Add("data-asynchAction", "/Home/SendMessage");
}

@using (@Html.BeginForm("SubmitTest", "Home", FormMethod.Post, attrs))
{
	@Html.AntiForgeryToken();
	<p>Sender</p> @Html.TextBox("Sender")
	<br />
	<p>Message</p>@Html.TextBox("Message")
	<br />
	<p>Anti forgey token:</p><input type="text" id="token" value="" />
	<hr />
	<input type="Submit" value="   Submit Form   " /> 
	<input type="button" value="   Send JSON    " id="sendJson" />
}
	
<br />

@section Scripts
{
	<script src="~/Scripts/App/common.js"></script>
	<script src="~/Scripts/App/antiforgery.js"></script>
}

The corresponding Javascript file code contains the method to send the JSON request and receive a JSON response:

$("#sendJson").click(function (event) {
	event.preventDefault();
	console.log("clicked");	
	var o = { sender: "Sid", Message: "Hello World!" };
	var jsonStr = JSON.stringify(o);
	var actionUrl = $("#TestForm").attr("data-asynchAction");
	console.log(actionUrl);
	var token = getVerificationToken('TestForm');
	console.log(token);
	$.ajax({
		url: actionUrl,
		type: "POST",
		headers: {
			"__RequestVerificationToken": token
		},
		data: jsonStr,
		dataType: "json",		
		contentType: "application/json; charset=utf-8"
	}).done(function (jsonResponse) {
		if (jsonResponse != null) {
			alert(jsonResponse.Message);
		}
	});
});

Notice we are sending the request verification token as part of the HTTP headers. "getVerificationToken()" is a method which reads the value of the hidden field generated by @Html.AntiForgeryToken() and is present in the common.js file (see source code).

Please note, the sample code present in the ZIP file reads the token from a TEXBOX which contains the valid token generated by @Html.AntiForgeryToken(). This just so that the value can be modified and tested with.

References

Chapters 7 & 15 from the book Professional ASP.NET MVC5 by Jon Galloway et all.

 

Thank you!

License

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

Share

About the Author

Siddharth R Barman
Software Developer (Senior)
United States United States
My personal website is at http://sbytestream.pythonanywhere.com

You may also be interested in...

Comments and Discussions

 
-- There are no messages in this forum --
Permalink | Advertise | Privacy | Cookies | Terms of Use | Mobile
Web01 | 2.8.190306.1 | Last Updated 23 Feb 2015
Article Copyright 2015 by Siddharth R Barman
Everything else Copyright © CodeProject, 1999-2019
Layout: fixed | fluid