swirl
Home Software Blog Wallpapers Webtools
Anti-forgery validator for HTTP Headers
Sunday 22, February 2015   |   Post link   |   Sample code  Download related meterial

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";
}

Test

@{ 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.

Sample code  Download related meterial


Load Comments

Posts By Year

2016 (6)
2017 (5)
2012 (2)
2013 (4)
2014 (2)
2015 (18)

Posts By Category

Software Engineering (1)
IIS (1)
T-SQL (1)
Git (1)
Technology (1)
Database migration (1)
PHP (1)
Corporate culture (1)
Tablet (1)
OData (1)
Containers (3)
Lucene (1)
Test Driven (1)
Java (2)
TDD (1)
Solr (1)
XML (1)
TSQL (5)
Windows (4)
PowerShell (1)
Software development (1)
Wallpapers (1)
Utilities (1)
Entity Framework (2)
Rants (5)
Life (5)
Programming (13)
Office (1)
Book review (3)
Sql Server (1)
ASP.NET MVC (4)
Android (1)
Desktop (1)
Unit Testing (1)

Posts By Tags

Dockers(2) Books(1) Organization(1) Busy(1) PowerShell(1) PHP(1) Backup(1) Database(1) Containers(3) Mobile Apps(1) Paths(1) SSH(1) Web Development(4) Yii(1) Debugging(1) Advertising(1) Adults(1) GradleApache(1) C#(2) SQL Code-first Migration(1) Developer(1) Consumerism(1) Log4J(1) Self reliance(1) Company culture(1) Entity framework(1) Work culture(1) Programming(1) Management(2) Book review(2) Validation(2) C++(1) Sql Express(1) HierarchyID(1) Lucene(1) IIS(1) Windows 10(1) Cmdlet(1) Intelli J(1) JavaScript(1) Spirituality(1) Migration history(1) Tablet(1) OData(1) TDD(1) Data-time(1) Beliefs(1) Checkbox(1) CTE(1) CSRF(1) JSON(1) Windows 8.1(1) Quality(1) XML(1) ASP.NET(4) TSQL(3) File history(1) Windows(1) Wallpapers(1) SD card(1) File copy(1) SQL(1) Windows 2016(2) Python(1) MVC(4) Url rewrite(1) Solr(1) Modern Life(1) System Image(1) Android(1) Commons(1) Office(1) Anti-forgery(1)