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!
}
