July 28, 2017

Prevent Cross Site Request Forgery


In the world of web development, most programmers are aware of the major security issues involving data transfer, between the user side and the server, and know how to prevent them (e.g: Javascript injections). There is still one that is mostly unknown, especially from beginners (and some veteran trust me...) and it is the Cross-Site Request Forgery. Let's see what it is and how we can prevent it.

What is it?

The best way to explain what a CSRF is, without using big terms is to use an example.

Let's say Kevin log into an online store, he buys an article and is prompt with a form to enter his credit card information that are later saved. After confirming his purchase, he opens another tab and start browsing the web without logging out of the online store.A couple of days later he realizes charges on his credit card that he didn't do, in one word Kevin got hacked.

Ok, let's analyze the situation to see where the CSRF happened:

  • after logging in, Kevin requested a form to put his credit card information and after his purchase, he didn't log out so from the server his session is still open.
  • Without his knowledge, Kevin browsed into a malicious website that will take his session variable from the store website, stored as a cookie and use this variable to submit some orders in the background(using mostly javascript).

That's where the forgery happened. The malicious website used the open session to appear as Kevin, in other word, from the server point of view, it received a valid session variable, and proceed to place the orders.

How to prevent it:

For PHP users, we can use a simple approach:

When the form is requested

  1. create a random string of characters
  2. store the string in a hidden input within the form and also in the session variable on the server side.

When the form is submitted

  1. send the user token along with the form information
  2. compare the user side token and the one stored in the session variable.
    • if equal, proceed with the form data
    • if not equal, reject the form data

Examples of implementation:

For PHP 5.6.0 or later

Create an hidden input in your form to store a random token

  <input id='token' type='hidden' value=<?php echo($token) ?>>

On the server side, Generate the token

<?php
	session_start();
	//create the token
	bin2hex(openssl_random_pseudo_bytes(32));
	//store the tokken into session variable					
	$_SESSION['token'] = $token;
?>

Verify the validity of the form before proceeding with the data

<?php
	session_start();
	//check token
	if (!hash_equals($_SESSION['token'], $_POST['token'])) {
		//CSRF detected destroy the session and  redirect the user					
		session_destroy();
		header("location:'home'");
	}
	// process the form data
?>

For .NET (MVC), there is a very simple way in C# to validate your form:

On the client side form, just put this instruction, usually at the end:

//code using '@Html' helper methods from  System.Web.Mvc namespace
@Html.AntiForgeryToken();

This will generate a hidden input where the value will be a random string of character like so:

Now, on your server side code, before treating the information from the form in the controller, you can do:

[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult saveCustomer(Customer customer)
{
	if(!ModelState.IsValid)				
	{
		///It's a forgery, remove all session variable
		///and redirect the user to the homepage
		Session.RemoveAll();
		return View("homePage");
	}
	///Process the form data
	///-----
	///-----
	return View();
}

With this code, when the form is submitted, 'ModelState.IsValid' will compare the user token against another one stored on the server.Basically the same concept as in PHP

In those examples, in case of a CSRF the form will NOT be treated and the user will be logged out from his session.

To sum up

This method is only one of many, to protect your form from CSRF attacks. Most modern browsers start to utilise other type of cookies , to prevent them, for example, you have 'httpOnly Cookies' that can't be accessed by client-side APIs like javascript, or 'SameSite Cookies' that can only be sent in request originating from the same origin as the target domain.

It is always a good practice to stay up to date regarding website site security in order to protect, as much as possible, the User.