Secure PHP forms (the easy way)

Technical
December 1, 2011 6:56 pm

I’m running a straight forward contact form on my worky website, danieldavies.co.uk.

In the year or so the site has been running it has received exactly 0 spam message through it, so I’m just going to explain the really simply method I use to “secure” this form, and any other PHP form I work with.

First, a disclaimer. This isn’t the best or only way to secure a form. What it is is a real simple way to implement a form that is accessible to your users and covers you from a number of simple exploits. Basically, we’re going to make sure that anyone who submits to a form has to have actually visited the page that the form was on.

The theory

This is a real simple Cross Site Request Forgery (CSRF) token. It works as follows. A user visits the webpage and is given a unique identifier for their request. This unique identifier is stored in the user’s session, and any form submitted must contain their unique identifier which is automatically posted as a hidden field.

So, if someone creates a form on another website, but submits their form to your site it will fail, because your site won’t have provided the unique token. This also cuts out a lot of spam bots that simply post to the same URL, rather than filling out the form like a real human. It’s not fool proof, a simple screen scrape would allow a determined script kiddy to create their unique key, and read it from your form, but it will cut out the majority spam. It will also keep your forms safe from the aforementioned CSRF attack.

The implementation

functions.php:

<?php
session_start();

function csrf_token()
{
	/**
	 *
	 * Generates a Cross Site Request Forgery token for a form. This is to
	 * prevent other websites from posting to your forms
	 *
	 * Usage:
	 * <input type="hidden" name="xsrf-token" value="<?php echo csrf_token(); ?-->" required />
	 *
	 */
	$token = gen_token();
	$_SESSION['token'] = $token;
	return $token;
}

function check_token($token)
{
	/**
	 * Checks that the provided token matches the token that was generated
	 * by csrf_token()
	 *
	 * Usage:
	 * if(check_token($_POST['xsrf-token'])) { do something } else { raise an error }
	 */
	if ($_SESSION['token'] == $token)
	{
		return true;
	}
	return false;
}

function gen_token($len = 32)
{
	/**
	 * Generates a token of length $len (number of characters in token)
	 */
	mt_srand( (double)microtime()*1000000 );
	$chars = array(
		'Q', '@', '8', 'y', '%', '^', '5', 'Z', '(', 'G', '_', 'O', '`',
		'S', '-', 'N', '<', 'D', '{', '}', '[', ']', 'h', ';', 'W', '.',
		'/', '|', ':', '1', 'E', 'L', '4', '&', '6', '7', '#', '9', 'a',
		'A', 'b', 'B', '~', 'C', 'd', '>', 'e', '2', 'f', 'P', 'g', ')',
		'?', 'H', 'i', 'X', 'U', 'J', 'k', 'r', 'l', '3', 't', 'M', 'n',
		'=', 'o', '+', 'p', 'F', 'q', '!', 'K', 'R', 's', 'c', 'm', 'T',
	    'v', 'j', 'u', 'V', 'w', ',', 'x', 'I', '$', 'Y', 'z', '*'
	);
	                             
	$numChars = count($chars) - 1; $token = '';
	                             
	# Create random token at the specified length
	for ($i = 0; $i < $len; $i++)
	{
	    $token .= $chars[ mt_rand(0, $numChars) ];
	}	                             
	return $token;
}

Your website/form:

<form id="form" action="./" method="post">
<input type="hidden" name="xsrf-token" value="<?php echo csrf_token(); ?>" />
<input type="submit" value="Submit" />
</form>

Your validation:

include 'functions.php';

if(check_token($_POST['xsrf-token']))
{
    \\ Token is OK!
} else {
    \\ Token is not OK!
}
This article was written by on Thursday, December 1, 2011 at 6:56 pm. You can follow any responses to this entry through the RSS feed. You can leave a response, or trackback from your own site. Tags:

Leave a Reply


XCOM: Enemy Unknown

October 21, 2012
XCOM: Enemy Unknown

XCOM: Enemy Unknown is the latest modernisation of a classic game developed by Firaxis and published by 2k. Although the franchise has a colourful history of cancelled games and relatively unheard of releases, it’s heyday was back in the mid-nineties with the original Enemy Unknown and the sequel, Terror from the Deep, being the titles that most gamers will be familiar with. Their premise was as simple then as it is now. Aliens have started invading a near future earth [...]

Continue Reading →

Asking for forgivness, not permission

July 19, 2012

Sometimes in life it’s easier to be forgiven for something than it is to get permission. It’s an approach I’ve been trying to adopt in my programming lately in a way that sometimes feels a little counter intuitive. If, for example, you need to ensure something going in to a database needs to be unique you may do something like this: def generate_slug(self): if not self.slug: self.slug = slugify(self.title) try: Page.objects.get(slug=self.slug) self.slug = self.slug + "_" self.generate_slug() except Page.DoesNotExist: self.save() [...]

Continue Reading →

Eurogamer Expo 2012

October 1, 2012
Eurogamer Expo 2012

2012 was my second year at Eurogamer, the first being last year, but it was no less spectacular second time round. I only went on the Saturday this year, mostly because I booked my ticket too late, but also because I wasn’t so interested in the games on offer this year. There were tonnes of triple A titles on show, as well as the usual mix of merchandise, retro gaming, t-shirts and the like. But if I’m honest, the games [...]

Continue Reading →

The Beat-Herder Festival – 2012

April 15, 2012
The Beat-Herder Festival – 2012

Starting to get quite excited about this year’s Beat-Herder Festival! 6 or so friends have confirmed, and I have my ticket + parking pass. More of the lineup has been trickling out  over the last few weeks and I’m most exited to be seeing Slamboree, Lee Scratch Perry and Parov Stelar. There’s also loads of stuff on that I’ve never heard of, so hopefully I’ll bring home some new names to listen to afterwards. Anyone who has not heard of [...]

Continue Reading →