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


Comments

Posts By Year

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

Posts By Category

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

Posts By Tags

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