How to stop forms on other sites submitting to your scripts
Archive - Originally posted on "The Horse's Mouth" - 2012-04-15 16:50:05 - Graham EllisFrom time to time, many of us web site authors and maintainers put a form on our site which submits data to another site. It might be something as simple as a Google seach box or a webmail login page.
If you're developing a page / form which you do not want to be filled in remotely in this way, you can add a hidden field to your form and check that this field really exists, and has the correct value, when you process the page's data. Unfortunately, this hdden field isn't hidden from the knowledgable user who's familiar with viewing the source of a web page, so this technique alone wont't stop the determined hacker. And such a hack, where it's unwelcome, is known as a Cross Site Request Forgery (CSRF) attack.
How can you prevent CSRF attacks? Rather than using a fixed hidden field in your form, you can prime your form with an upredicatble, and probably unique, hidden field value. Then you check each form that's submitted to your web site to check that it has the correct hidden field value.
Here's the various elements of the code (in PHP - in the order they are run!)
1. Create a unique ID and add it onto the end of a file of active IDs
$uid = uniqid("xz");
$fh = fopen("../keys.txt","a");
fputs($fh,"$uid\n");
fclose($fh);
2. Include a unique ID within a hidden field on the form
<input type=hidden name=bov value=' . $uid . '>
3. When the form is submitted, check that the hidden field is one of the allowed vales in the file (we have done this in two stages):
if (preg_match('/^xz/',$_POST[bov])) {
$mykeys = file("../keys.txt");
$psn = array_search($_POST[bov] . "\n",$mykeys) ;
if ($psn !== FALSE ) {
4. And if the key is one of the ones that's allowed, remove it from the list (to avoid people setting up a form with a constant value because it worked once!), reduce the list to 40 elements (this is bookkeeping to avoid the valid ID file growing for ever), rewrite the reduced valid keys file, and perform the action required.
array_splice($mykeys,$psn,1);
array_splice($mykeys,0,count($mykeys)-40);
$fh = fopen("../keys.txt","w");
fputs($fh,implode("",$mykeys));
fclose($fh);
# Action on data here
The sample code above has been added to our Melksham diary submission form in order to prevent double submissions (using back and submit buttons) and to prevent people putting submit form on to their own site. It's really pretty unlikely that a form such as this would be worth duplicating elsewhere for manual entry, but we're also preventing automata that find it and automatically keep filling it in from getting through.
Cross Site Request Forgery Protection isn't just needed on a few sites - it's pretty universal. And so many frameworks have it built in. There's a Python / Django module, for example, described [here]. And further reading is available [here] and [here].