Building A Cross-Site Request Forgery Proof WebApp With AJAX ASP.Net MVC

ASP.Net

Cross-Site Request Forgery (CSRF) is an attack that forces an end user to execute unwanted actions on a web application in which they’re currently authenticated.

CSRF Overview

CSRF is an attack that lures currently authenticated victim into submitting a malicious request from a malicious web site. Since browser requests automatically include any credentials associated with the site, such as the user’s session cookie, the request is sent as a logged in user request.

Having SSL does not prevent a CSRF attack, because the malicious site can send an “https://” request.

 

Example

There are numerous ways in which an end user can be tricked into loading information from or submitting information to a web application. Let’s look at this typical example – John intends to send $200 to James using the examplebank.com web application that is vulnerable to CSRF. However, Mark an attacker want to trick John into sending the money to Mark. Let’s take a look at how Mark can achieve this malicious act.

HttpGet Method

If the bank app support get request to transfer money then, the following request can be used by Mark

GET http://examplebank.com/nip/send?acct_no=12345=amount=200&acct_name=mark" HTTP/1.1

This can be embedded in a malicious email, so the csrf request is triggered as soon as the email is opened unknowingly for John who received the email. In this case John doesn’t have to click on any link for this to happen.

The attacker can also send a link that needed to be clicked by tricking John into clicking it. The following tag can be send:

<a href="http://examplebank.com/nip/send?acct_no=1234&amount=200&acct_name=mark">Click here to claim your price</a>

HttpPost Method

Let’s say the bank now uses POST and the vulnerable request looks like this:

POST GET http://examplebank.com/nip/send HTTP/1.1
acct_no=12345&amount=200&acct_name=mark

Mark’s malicious site with contain the following HTML form:

<h1>You Are a Winner!</h1>
  <form action="http://examplebank.com/nip/send" method="post">
    <input type="hidden" name="acct_no" value="12345" />
    <input type="hidden" name="acct_name" value="mark" />
	<input type="hidden" name="amount" value="200" />
  <input type="submit" value="Click here to claim your price"/>
</form>

Clicking on the link triggers the request to run on the server with the user’s authentication context, and can do anything that an authenticated user is allowed to do.

Prevent CSRF with Anti-Forgery Tokens

In ASP.NET MVC anti-forgery tokens also called request verification tokens are used to prevent Cross-Site Request Forgery.

Adding Anti-Forgery Tokens with Html helpers in ASP.NET MVC

The anti-forgery token can be added to a Razor page using the HtmlHelper.AntiForgeryToken helper method:

 @Html.AntiForgeryToken()

The full form will look like this:

@using (Html.BeginForm("PostData", "Account", FormMethod.Post,new {id="transferForm" }))
{
    @Html.AntiForgeryToken()
    <div class="col-md-4">
        <label>Name</label>
        <input type="text" id="name" class="form-control" />
        <label>Title</label>
        <input type="text" id="title" class="form-control" />
        <input type="button" id="saveBtn" class="btn btn-primary" value="Save Data" />
    </div>
}

This generate the following in the browser:

<form action="/Account/PostData" id="transferForm" method="post">
		<input name="__RequestVerificationToken" type="hidden" value="NFry8i9WGCFUfYUYzp32zsGNlqfpd1WWsKohA5FL-I9aT278ECQ9m9h9D0nxer2T9DKmbDO7M1wOrjRH1eLg533YyQTBQBJwHD60aU-luao1">    <div class="col-md-4">
        
		<label>Name</label>
        <input type="text" id="name" class="form-control">
        <label>Title</label>
        <input type="text" id="title" class="form-control">
        <input type="button" id="saveBtn" class="btn btn-primary" value="Save Data">
    </div>
</form>

An input tag named __RequestVerificationToken is generated with the token. This will be validated on the server side when the form is submitted by decorating the ActionResult with atrribute like so:

[ValidateAntiForgeryToken()]
public ActionResult PostData(Person data)
{
		///.....
}

Adding Anti-Forgery Tokens to AJAX Request in ASP.NET MVC

Adding anti-forgery token to a request that is sent via AJAX is totally different since the request will not be sent by the form but via AJAX in JSON format.

In this scenario, the anti-forgery token will be generated on the client or the server side by calling AntiForgery.GetTokens. Let’s say I want to send some person data to my server via AJAX and I want to prevent CSRF, all I need to send the tokens in a custom HTTP header. Below is an example.

The regular input for the data I want to send:

<div class="container">
    <div class="col-md-4">
        <label>Name</label>
        <input type="text" id="name" class="form-control" />
        <label>Title</label>
        <input type="text" id="title" class="form-control" />
        <input type="button" id="saveBtn" class="btn btn-primary" value="Save Data" />
    </div>
</div>

Notice that the inputs are not within an html form since the data will be sent via AJAX.

The javascript code look like this:

csrf-ajax-asp-net-mvc

From the ajax post function, I have included a custom header named RequestVerificationToken and set the value to be the computed token sent from the Controller to the view via ViewBag. Lets take a look at the server side code:

 public ActionResult Index()
 {
    //Simulate authentication
    FormsAuthentication.SetAuthCookie("mark", true);
    ViewBag.RequestToken = antiForgeryToken();
    return View();
  }
//Generate the token
  string antiForgeryToken()
  {
     AntiForgery.GetTokens(null, out string cookieToken, out string formToken);
     return cookieToken + ":" + formToken;
  }

The code is self explanatory so I wont be going over to explain. Let’s take a look at the Postdata method:

public ActionResult PostData(Person data)
{
	var exMessage = string.Empty;
	try
	{
		string cookieToken = "";
		string formToken = "";
		var tokenHeader = Request.Headers.Get("RequestVerificationToken");
		if (string.IsNullOrWhiteSpace(tokenHeader)) return Json(new { error = true }, JsonRequestBehavior.AllowGet);
		string[] tokens = tokenHeader.Split(':');
		if (tokens.Length == 2)
		{
			cookieToken = tokens[0].Trim();
			formToken = tokens[1].Trim();
		}
		//This throws exception if token is not valid
		AntiForgery.Validate(cookieToken, formToken);
		return Json(new { error = false }, JsonRequestBehavior.AllowGet);
	}
	catch (Exception ex)
	{
		exMessage = ex.Message;
	}
	return Json(new { error = true, message = exMessage }, JsonRequestBehavior.AllowGet);
}

As you can see in the code, I fetched the token from the request header and then call the AntiForgery.Validate. This method is void which means that it doesn’t return anything but it will throw exception if the token is not valid.

That is it guys. I hope this helps.

 

Similar Posts