Secure Your Forms With Form Keys

Secure Your Forms With Form Keys

Security is a hot topic. Ensuring that your websites are secure is extremely important for any web application. In fact, I spend 70% of my time securing my applications. One of the most important things we must secure are forms. Today, we are going to review a method to prevent XSS (Cross-site scripting) and Cross-site request forgery on forms.

Why?

POST data can be sent from one website to another. Why is this bad? A simple scenario…

A user, logged into your website, visits another website during his session. This website will be able to send POST data to your website — for example, with AJAX. Because the user is logged in on your site, the other website will also be able to send post data to secured forms that are only accessible after a login.

We also must protect our pages against attacks using cURL

How Do We Fix This?

With form keys! We’ll add a special hash (a form key) to every form to make sure that the data will only be processed when it has been sent from your website. After a form submit, our PHP script will validate the submitted form key against the form key we’ve set in a session.

What We Must Do:

  1. Add a form key to every form.
  2. Store the form key in a session.
  3. Validate the form key after a form submit.

Step 1: A Simple Form

Login Form

First we need a simple form for demonstration purposes. One of the most important forms we have to secure is the login form. The login form is vulnerable tobrute force attacks. Create a new file, and save it as index.php in your web-root. Add the following code within the body:

	<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
	<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
	<head>
		<meta http-equiv="content-type" content="text/html;charset=UTF-8" />
		<title>Securing forms with form keys</title>
	</head>
	<body>
		<form action="" method="post">
		<dl>
			<dt><label for="username">Username:</label></dt>
			<dd><input type="text" name="username" id="username" /></dd>
			<dt><label for="username">Password:</label></dt>
			<dd><input type="password" name="password" id="password" /></dd>
			<dt></dt>
			<dd><input type="submit" value="Login" /></dd>
		</dl>
		</form>
	</body>
	</html>

Now we have a simple XHTML page with a login form. If you want to use form keys on your website, you can replace the script above with your own login page. Now, let’s continue to the real action.

Step 2: Creating a Class

We are going to create a PHP class for our form keys. Because every page can contain only one form key, we could make a singleton of our class to make sure that our class is used correctly. Because creating singletons is a more advanced OOP topic, we will skip that part. Create a new file called formkey.class.php and place it in your web-root. Now we have to think about the functions we need. First, we need a function to generate a form key so we can place it in our form. In your PHP file place the following code:

<?php

//You can of course choose any name for your class or integrate it in something like a functions or base class
class formKey
{
	//Here we store the generated form key
	private $formKey;
	
	//Here we store the old form key (more info at step 4)
	private $old_formKey;
	
	//Function to generate the form key
	private function generateKey()
	{
		
	}
}
?>

Above, you see a class with three parts: two variables and a function. We make the function private because this function will only be used by our outputfunctions, which we will create later. In the two variables, we will store the form keys. These are also private because they may only be used by functions inside our class.

Now, we have to think of a way to generate our form key. Because our form key must be unique (otherwise we don’t have any security), we use a combination of the users IP-address to bind the key to a user, mt_rand() to make it unique, and the uniqid() function to make it even more unique. We also encrypt this information with md5() to create a unique hash which we can then insert into our pages. Because we used md5(), a user cannot see what we used to generate the key. The whole function:

//Function to generate the form key
private function generateKey()
{
	//Get the IP-address of the user
	$ip = $_SERVER['REMOTE_ADDR'];
	
	//We use mt_rand() instead of rand() because it is better for generating random numbers.
	//We use 'true' to get a longer string.
	//See http://www.php.net/mt_rand for a precise description of the function and more examples.
	$uniqid = uniqid(mt_rand(), true);
	
	//Return the hash
	return md5($ip . $uniqid);
}

Insert the code above into your formkey.class.php file. Replace the function with the new function.

Step 3: Inserting a Form Key into Our Form

For this step, we create a new function that outputs a hidden HTML field with our form key. The function consists of three steps:

Generate, Save, Output

  1. Generate a form key with our generateKey() function.
  2. Store the form key in our $formKey variable and in a session.
  3. Output the HTML field.

We name our function outputKey() and make it public, because we have to use it outside of our class. Our function will call the private function generateKey() to generate a new form key and save it locally in a session. Lastly, we create the XHTML code. Now add the following code inside our PHP class:

//Function to output the form key
public function outputKey()
{
	//Generate the key and store it inside the class
	$this->formKey = $this->generateKey();
	//Store the form key in the session
	$_SESSION['form_key'] = $this->formKey;
	
	//Output the form key
	echo "<input type='hidden' name='form_key' id='form_key' value='".$this->formKey."' />";
}

Now, we are going to add the form key to our login form to secure it. We have to include the class in our index.php file. We also have to start the session because our class uses sessions to store the generated key. For this, we add the following code above the doctype and head tag:

<?php
//Start the session
session_start();
//Require the class
require('formkey.class.php');
//Start the class
$formKey = new formKey();
?>

The code above is pretty self-explanatory. We start the session (because we store the form key) and load the PHP class file. After that, we start the class with new formKey(), this will create our class and store it in $formKey. Now we only have to edit our form so that it contains the form key:

<form action="" method="post">
<dl>
	<?php $formKey->outputKey(); ?>
	<dt><label for="username">Username:</label></dt>
	<dd><input type="text" name="username" id="username" /></dd>
	<dt><label for="username">Password:</label></dt>
	<dd>input type="password" name="password" id="password" /></dd>
<dl>
</form>

And that’s all! Because we created the function outputKey(), we only have to include it in our form. We can use form keys in every form by just adding <?php $formKey->outputKey(); ?> Now just review the source of your webpage and you can see that there is a form key attached to the form. The only remaining step is to validate requests.

Step 4: Validating

We won’t validate the whole form; only the form key. Validating the form is basic PHP and tutorials can be found all over the web. Let’s validate the form key. Because our “generateKey” function overwrites the session value, we add a constructor to our PHP class. A constructor will be called when our class is created (or constructed). The constructor will store the previous key inside the class before we create a new one; so we’ll always have the previous form key for validating our form. If we didn’t do this, we wouldn’t be able to validate the form key. Add the following PHP function to your class:

//The constructor stores the form key (if one exists) in our class variable.
function __construct()
{
	//We need the previous key so we store it
	if(isset($_SESSION['form_key']))
	{
		$this->old_formKey = $_SESSION['form_key'];
	}
}

A constructor should always be named __construct(). When the constructor is called we check if a session is set, and if so, we store it locally in our old_formKey variable.

Now we are able to validate our form key. We create a basic function inside our class which validates the form key. This function should also be public because we are going to use it outside our class. The function will validate the POST value of the form key against the stored value of the form key. Add this function to the PHP class:

//Function that validated the form key POST data
public function validate()
{
	//We use the old formKey and not the new generated version
	if($_POST['form_key'] == $this->old_formKey)
	{
		//The key is valid, return true.
		return true;
	}
	else
	{
		//The key is invalid, return false.
		return false;
	}
}

Within index.php, we validate the form key by using the function we just created in our class. Of course, we only validate after a POST request. Add the following code after $formKey = new formKey();

$error = 'No error';

//Is request?
if($_SERVER['REQUEST_METHOD'] == 'post')
{
	//Validate the form key
	if(!isset($_POST['form_key']) || !$formKey->validate())
	{
		//Form key is invalid, show an error
		$error = 'Form key error!';
	}
	else
	{
		//Do the rest of your validation here
		$error = 'No form key error!';
	}
}

We created a variable $error which stores our error message. If a POST request has been sent we validate our formkey with $formKey->validate(). If this returns false, the form key is invalid and we display an error. Note that we only validate the form key — you’re expected to validate the rest of the form yourself.

In your HTML, you can place the following code to show the error message:

	<div><?php if($error) { echo($error); } ?></div>

This will echo the $error variable if it is set.

Form

If you start your server and go to index.php, you will see our form and the message ‘No error’. When you submit a form you will see the message ‘No form key error’ because it is an valid POST request. Now try to reload the page and accept when your browser requests that the POST data be sent again. You will see that our script triggers an error message: ‘Form key error!’ Your form is now protected against input from other websites and errors with page reloads! The error is also shown after a refresh because a new form key was generated after we submitted the form. This is good because, now, user can’t accidentally post a form twice.

Full Code

Here is the whole PHP and HTML code:

index.php

<?php
//Start the session
session_start();
//Require the class
require('formkey.class.php');
//Start the class
$formKey = new formKey();

$error = 'No error';

//Is request?
if($_SERVER['REQUEST_METHOD'] == 'post')
{
	//Validate the form key
	if(!isset($_POST['form_key']) || !$formKey->validate())
	{
		//Form key is invalid, show an error
		$error = 'Form key error!';
	}
	else
	{
		//Do the rest of your validation here
		$error = 'No form key error!';
	}
}
?>
	
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
	<meta http-equiv="content-type" content="text/html;charset=UTF-8" />
	<title>Securing forms with form keys</title>
</head>
<body>
	<div><?php if($error) { echo($error); } ?>
	<form action="" method="post">
	<dl>
		<?php $formKey->outputKey(); ?>

		<dt><label for="username">Username:</label></dt>
		<dd><input type="text" name="username" id="username" /></dd>
		<dt><label for="username">Password:</label></dt>
		<dd><input type="password" name="password" id="password" /></dd>
		<dt></dt>
		<dd><input type="submit" value="Submit" /></dd>
	<dl>
	</form>
</body>
</html>

fomrkey.class.php

<?php

//You can of course choose any name for your class or integrate it in something like a functions or base class
class formKey
{
	//Here we store the generated form key
	private $formKey;
	
	//Here we store the old form key (more info at step 4)
	private $old_formKey;
	
	//The constructor stores the form key (if one excists) in our class variable
	function __construct()
	{
		//We need the previous key so we store it
		if(isset($_SESSION['form_key']))
		{
			$this->old_formKey = $_SESSION['form_key'];
		}
	}

	//Function to generate the form key
	private function generateKey()
	{
		//Get the IP-address of the user
		$ip = $_SERVER['REMOTE_ADDR'];
		
		//We use mt_rand() instead of rand() because it is better for generating random numbers.
		//We use 'true' to get a longer string.
		//See http://www.php.net/mt_rand for a precise description of the function and more examples.
		$uniqid = uniqid(mt_rand(), true);
		
		//Return the hash
		return md5($ip . $uniqid);
	}

	
	//Function to output the form key
	public function outputKey()
	{
		//Generate the key and store it inside the class
		$this->formKey = $this->generateKey();
		//Store the form key in the session
		$_SESSION['form_key'] = $this->formKey;
		
		//Output the form key
		echo "<input type='hidden' name='form_key' id='form_key' value='".$this->formKey."' />";
	}

	
	//Function that validated the form key POST data
	public function validate()
	{
		//We use the old formKey and not the new generated version
		if($_POST['form_key'] == $this->old_formKey)
		{
			//The key is valid, return true.
			return true;
		}
		else
		{
			//The key is invalid, return false.
			return false;
		}
	}
}
?>

Conclusion

Adding this code to every important form on your website will increase your form’s security dramatically. It even stops refreshing issues, as we saw in step 4. Because the form key is only valid for one request, a double post is not possible.

This was my first tutorial, I hope you like it and use it to improve your security! Please let me know your thoughts, via the comments. Have a better method? Let us know.

Further Reading


Note: Want to add some source code? Type <pre><code> before it and </code></pre> after it. Find out more
  • http://joaopedropereira.com/ João Pedro Pereira

    Good tutorial :). That’s an easy way that I’ve already used in two projects to not use captcha’s.

  • http://samiuljahan.wordpress.com samiul

    Hi I want to see a scrrencasts of how to create a login form using facebox?

    • Thomas

      Not sure this would quite make a tutorial….

  • Tanax

    Why did you use visibility scopes for all functions except the constructor?

    • http://www.mai-ai.org Wouter Bulten

      First answer (and most simple): I forgot it.

      Second: It is not really needed. Only when I create singletons or private classes I make them private.

      • Tanax

        Point 1: Understandable!
        Point 2: You do know that there is a public visibility scope aswell? And since __construct is a PHP5 method, why not use the rest of the PHP5 syntax – such as the visibility scopes. Especially since the rest of the methods use it in the class?

        Sorry for criticising xD

  • http://mwawrzyniak.pl Marcin

    Simply awesome! thank you v. much!

  • http://thedesigned.com Greg Dougherty

    I was looking for something like this the other day! Glad I found it!!!

    - Greg

  • MoldyMagnet

    Terrible captcha, so hackable. How about a tutorial on how to implement a re-captcha? Or at least something isn’t just unobfacusated text on a plain background.

  • http://ramon.com.ua Ramon

    Nice! Thank you. Gonna use it in my control panel.

  • http://www.voveris.eu/ Faifas

    I still don’t get a idea, why a hacker could not read the HTML source which already has your generated unique key and then submit it with all other form values?

  • http://www.goingson.be Rob

    Just curious not convinced!

    Is the aim to make sure that the POST data is coming from your site and not from some script on another server?

    This method also could apply to making images also enforced to your site only and deny hotlinking BUT…..

    Wouldn’t also a Captcha prevent massive submits?

    Additionally, maybe I missed it but since you output a “hidden” field with the value in it, would I not be able to use CURL get the form from you site, read in the hidden value, and re submit?

    Again I think a good captcha would be 1000 x more effective.

    As someone said numerous times before here, use recaptcha or something similar. This method here is more work and not safer.

    • Thomas

      This is to prevent the issues related to catchpa. And unless you had curl cookies that wouldn’t work. This makes it a bit harder for hackers.

    • http://www.mai-ai.org Wouter Bulten

      What do you think about login forms?

      I would not think about using a captcha for a login form.

  • http://www.yonego.nl/ Wesley van Opdorp

    I like the tutorial, great job! As a fellow dutch programmer I think its great to see some good php tutorials from the Netherlands!

    One thing I’d like to comment on is leaving action empty. Certain browsers (including Google’s chrome) use the base href or domain name as action address when you do not provide one. This can cause you to miss out on important business leads at a contact form for example.

    Keep up the good work!

  • http://www.nouveller.com/ Benjamin Reid

    I’ll be implementing this from now, it makes me feel stupid not knowing about if beforehand though, haha.

  • Lucho

    Good tut but using plain md5 is considered vulnerable these days. Recommendations are using sha256 or combination of hash algorithms(md5+hmac) instead.

    • http://www.mai-ai.org Wouter Bulten

      I know, now, but I wrote this tutorial about 6 months ago..

      • Tom

        It hardly matters that md5 is insecure, because the point of the article was not hiding a password or other data.

  • http://ramon.com.ua Ramon

    There is an error
    index.php, 12 line
    if($_SERVER['REQUEST_METHOD'] == ‘post’)

    it must be
    if($_SERVER['REQUEST_METHOD'] == ‘POST’)

  • http://www.dbsearch.co.uk Ersin Demirtas

    Thanks very helpgiuful article…

  • Dwayne Clarke

    This is not good for XSS or CSRF, since i can view the html in the broswer create the same page and submit it or use telnet.

    here is what u need to know http://phpsec.org/projects/guide/

    The corner stone of web security is filtering all user input u get from the user especially when your client is interacting with a database database or you allow your client to post links which other users can click on.

    a quick note:

    (Problem – Counter measure)

    Spoofed Form Submissions/XSS/CSRF/Spoofed HTTP Requests – 1. Filter data from users (using regular expression, htmlentities, etc).

    Exposed Access Credentials – 1. use .htaccess files if you are not using a dedicated server other wise edit your php.ini file to allow/deny access to intended folders

    SQL Injection – 1. prepared SQL statements 2. mysql_real_escape_string() function 3. filter data from form thus data that will be placed in SQL statements)

    Session Hijacking/Session Fixation – 1. disable PHPSESSID in URLs 2. regenerate session id when user access level changes (after logging into website)

  • Dwayne Clarke

    quick note:

    1. If you are saving a session id it should also be placed in a database preferably.

    2. When storing a user table for logging into web apps password hashing should be used rather than having plain-text passwords exposed.

    http://phpsec.org/articles/2005/password-hashing.html

  • Jeff

    In step 2, you briefly mention that a singlton form key would be the best thing to do since each page can have only one key. That isn’t a singleton at all. If one would turn that class into a true singleton, you would never be able to generate more than 1 form key. So every form would have to use the same key. If you want to be able to have a different key per page/form you would be better with some sort od factory class that would be responsible for handing out the right key to the right page/form as well as lifetime managent. You would probably want keys that have been issued to expire with sessions or possibly time intervals.

    Just some thoughts anyway.

  • http://www.allisclear.com Stefan Ashwell

    Really nice tutorial!

  • bybye

    hmm, im wondering if this helps…

    if you can post data via ajax, you can also get data via ajax.
    if you get the form via ajax then you have the formKey and you can do a post with this formKey… in my opinion this only helps to prevent simple XSS attacks, but if someone wants to – he can..

  • Jomaguir

    Thanks man, that’s so helpful, right now I’m working in a website and it’s neccessary to improve the security. That helps a lot and I liked the tutorial, keep on so…

  • SX

    I learn from the tutorials and the comments. Keep those criticism coming. This is the wild wild web.

  • Zoran

    About more PHP security check this website: http://shiflett.org/articles
    Articles there are amazing

  • damulag84

    I used Zend Captcha and Zend Session in securing all my forms. Zend Captcha generates an alphanumeric string, I put the value in a session captcha using Zend Session and put the the value in a hidden captcha field in all my forms.

    Once the form is submitted, I validate the hidden POST captcha by comparing it to the captcha session value. If match I process the transaction, if not I put an error message. Once this validation is done, I destroyed the session captcha and generate a new one. In this way the hidden captcha value cannot be used again.

  • http://www.protechmate.com.my ilyasishak

    great tuts..
    & great comments too!
    good job Wouter!

  • http://www.rayshoo.com billy

    thanks. very good tutorial.

  • http://www.rayshoo.com chad

    this is really helpful. thanks for sharing.

  • http://www.jsxtech.com Jaspal Singh

    Excellent tutorial for creating secure php forms, Thanks for sharing.

  • http://www.jogjapenatahcinta.com Mori

    Nice tutorials :)

  • http://www.professdiamond.com เพชร

    thanks, I’ve learned something new.

  • Davi

    if($_POST['form_key'] == $this->old_formKey)

    If both the session and post variable are empty, they’re also equal. So if you’re attacking the form you simply omit the post variable.

    • Proc

      if($_POST['form_key'] == $this->old_formKey && isset($_POST['form_key']))

      Not tested.

      • Phil

        Kind of old I know, but testing goes from left to right, this would generate a warning about trying to get a nonexistent key in the post variable, should be

        if (isset($_POST['form_key']) && $_POST['form_key'] == $this->old_formKey)

    • rwhirn

      You all are missing the validation with index.php. If form_key is not posted it will never get to the validate function. Line 13 of index.php:
      if(!isset($_POST['form_key']) || !$formKey->validate())

  • Yemmy

    How can I get it in PDF

  • Yemmy

    How can I get the lattest version of PHP Tutorial in PDF

  • K. Mirchev

    The improved solution is to generate field key name (i.e not only hidden field value but hidden field name) also and holds it in the session.

  • http://petscancun.com Apolo Castro

    Congratulation and thanks for sharing

  • dimtiris

    Hi ,i’m new to php and i would like some help …I hava copied and pasted the code and saved it as it says….but i’m getting an error …

    Fatal error: Call to undefined method formKey::outputKey() in C:\xampp\htdocs\index.php on line 38

    somebody help please thank you!!

  • http://www.blogwongjowo.blogspot.com rizal aditya

    Wow its great.. I just want to study for my final project. can u help me ?? :D

  • pyclepymbopeCramege

    Reins urinaire la forums, bactrim forte de mettre vos enfants de dГ©ontologie

    Grossesses non dГ©sirГ©es, la circulation du virus korean ginseng tea le commentaire soins

    Tres vite effexor 75 et internationalement 2 4 6

    Withdrawal symptoms cialis levitra a fait carriГЁre dans lexpГ©rimentation animale

    Ionisateur d horizons bio cla de souci avec l installation du lit, les 577

    Gaz anesthГ©siants pour chacune ibuprofen 400 de clinique aujourdhui nГ©cessaire, elle doit

    Monstrueux corbeaux rampants aspirin plus c de situations dexception et charger des

    Lentement acai burn et chimie organique de comprimГ©s ou vous un poteau j

  • http://link name

    comment3,

  • http://www.pixeloution.com Erik W

    I see so many people comparing this to captcha, which it is not. I think showing the login form an an example is possibly leading to confusion.

    A formkey will protect your logged in user from being exploited once he leaves your website.

    Example: I log in to example.com. I leave example.com (still logged in) and go to hacker.com. Hacker.com submits post data to a form only an authenticated user has access to and does something nasty. He can do this from his site because the user is still logged in at your site.

    A formkey will prevent this entirely.

    I’m not sure why one would use a formkey on a login page at all – seeing as it has no effect. One could still code a script to bombard that login form with logins looking to brute force it. Emulating a browser, including storing cookies to it works with the sessions, is easy.

  • http://twitter.com/lockitdownjms JS

    How does this protect against man in the middle attacks?!?!
    What is to stop someone from grabbing the key during transmission?

    • Ryan Gillett

      That’s a completely different security issue.

  • Nikul

    Thank you so much. I am new to this stuff, but understood all of it.
    And it really made my work easy

    Thanks once again.

  • Les

    The idea is to use a randomly generated key on each page load and I don’t see this here.

    To work with page redirects, etc what I do is to retain a queue of those randomly generated keys, say 3 or 4 in a session and if any given POSTed key is in the queue, allow the submit.

    This queue works like a FILO (First in, Last out) stack, where every newly generated key is first in (a new key is generated regardless (GET or POST) on every request, otherwise the queue becomes stale and therefor pointless – defeats the point.

  • http://www.floqom.nl FloQom

    Verry usefull
    Thank you

  • gerardo

    Excelent script..

    I found something similar

    http://blog.unijimpe.net/evitar-doble-post-en-php/

    regards

  • http://www.lightrains.com Nikhil Mohan

    What about this guys… ?

    ci = & get_instance();
    $this->ci->load->library(‘session’);
    $this->ci->load->library(‘form_validation’);

    if($this->ci->session->userdata(‘formkey’))
    {
    $this->old_formkey = $this->ci->session->userdata(‘formkey’);
    }
    }

    private function generate_key()
    {
    $ip = $this->ci->input->ip_address();
    $uniqid = uniqid(mt_rand(), true);
    return md5($ip . $uniqid);
    }

    public function render_field()
    {
    $this->formkey = $this->generate_key();
    $this->ci->session->set_userdata(‘formkey’, $this->formkey);
    return form_hidden(‘formkey’, $this->formkey);
    }

    public function validate()
    {
    if($this->ci->input->post(‘formkey’) == $this->old_formkey)
    {
    return TRUE;
    }
    else
    {
    $this->ci->form_validation->set_message(‘_check_formkey’, ‘%s is wrong!’);
    return FALSE;
    }
    }

    }

    /* End of file */

  • http://iarmar.com ibrahim azhar armar

    Very nice, i was getting irked by the resubmission of form via refresh. this has helped me a lot securing the form..

  • Sam

    Nice Idea!

  • http://fuzedbulb.com Asim

    Hi. Nice tutorial – however how will this method work when a user is browsing in multiple browser tabs?

    To illustrate, say we have two forms (e.g. Creating a new user and editing an existing user) opened one after the other in two tabs. The later form will overwrite the ‘form key’ in the session invalidating the first form.

    What do you think?

    • Phil

      Old I know. You shouldn’t be allowing someone to sign in or register and be signed in already, that would be a disaster. However to go with say someone having account settings open and open a contact form for help with a issue they have saw then you should use something along the lines of

      $page = (isset($_SERVER["REQUEST_URI"])) ? str_replace(“?” . $_SERVER["QUERY_STRING"], $_SERVER["REQUEST_URI"] : “0″;

      $_SESSION["form_key"] = md5($page . “_SALTED”);

      Simple solution how ever this does still have security issues as with all things, and REQUEST_URI is geared towards Apache and not for something like IIS, you can google workarounds if that is your case.

  • BB

    Parse error: parse error, unexpected T_STRING, expecting T_OLD_FUNCTION or T_FUNCTION or T_VAR or ‘}’ in formkey.class.php on line 3

  • Ofer

    Hi,

    I am not sure this works when the User may submit twice (clicks submit, waits a few seconds and submits again) though I have not checked with the above code.

    Example:

    Click submit on form_A (the keys are compared and OK)

    Now consider the point in time where execution passes the code where form_B’s formKey is set to $_SESSION['form_key'] ‘but’ form_B is not yet displayed on the User’s screen (I believe there is such an interval).

    During this interval, submit on Form_A is clicked again.

    Now we would be comparing the POSTed formKey from form_A with $_SESSION['form_key'] from form_B.

    If anyone overcame this, please let me know.

    Thanks

    • Tom

      Yes, use a re-submit key.