Recipe 9.13. Guarding Against Multiple Submission of the Same Form


9.13.1. Problem

You want to prevent a user from submitting the same form more than once.

9.13.2. Solution

Include a hidden field in the form with a unique value. When validating the form, check if a form has already been submitted with that value. If it has, reject the submission. If it hasn't, process the form and record the value for later use. Additionally, use JavaScript to disable the form Submit button once the form has been submitted.

Example 9-23 uses the uniqid( ) and md5( ) functions to insert a unique ID field in a form. It also sets the form's onsubmit handler to a small bit of JavaScript that disables the submit button once the form's been submitted.

Insert a unique ID into a form

<form method="post" action="<?php echo $_SERVER['SCRIPT_NAME'] ?>"       onsubmit="document.getElementById('submit-button').disabled = true;"> <!-- insert all the normal form elements you need --> <input type='hidden' name='token' value='<?php echo md5(uniqid()) ?>'/> <input type='submit' value='Save Data' id='submit-button'/> </form> 

Example 9-24 checks the submitted token against saved data in an SQLite database to see if the form has already been submitted.

Checking a form for resubmission

if ($_SERVER['REQUEST_METHOD'] == 'POST') {     $db = new PDO('sqlite:/tmp/formjs.db');     $db->beginTransaction();     $sth = $db->prepare('SELECT * FROM forms WHERE token = ?');     $sth->execute(array($_POST['token']));     if (count($sth->fetchAll())) {         print "This form has already been submitted!";         $db->rollBack();     } else {         /* Validation code for the rest of the form goes here --          * validate everything before inserting the token */         $sth = $db->prepare('INSERT INTO forms (token) VALUES (?)');         $sth->execute(array($_POST['token']));         $db->commit();         print "The form is submitted successfully.";     } } ?>

9.13.3. Discussion

For a variety of reasons, users often resubmit a form. Usually it's a slip-of-the-mouse: double-clicking the Submit button. They may hit their web browser's Back button to edit or recheck information, but then they re-hit Submit instead of Forward. It can be intentional: they're trying to stuff the ballot box for an online survey or sweepstakes. Our Solution prevents the non-malicious attack and can slow down the malicious user. It won't, however, eliminate all fraudulent use: more complicated work is required for that.

The Solution does prevent your database from being cluttered with too many copies of the same record. By generating a token that's placed in the form, you can uniquely identify that specific instance of the form, even when cookies are disabled. The uniqid( ) function generates an acceptable one-time token. The md5( ) function doesn't add any additional randomness to the token, but restricts the characters that could be in it. The results of uniqid( ) can be a mix of different letters and other characters. The results of md5( ) consist only of digits and the letters abcdef. For English-speaking users at least, this ensures that the token doesn't contain any naughty words.

It's tempting to avoid generating a random token and instead use a number one greater than the number of records already in your database table. There are (at least) two problems with this method. First, it creates a race condition. What happens when a second person starts the form before the first person has completed it? The second form will then have the same token as the first, and conflicts will occur. This can be worked around by creating a new blank record in the database when the form is requested, so the second person will get a number one higher than the first. However, this can lead to empty rows in the database if users opt not to complete the form.

The other reason not do this is because it makes it trivial to edit another record in the database by manually adjusting the ID to a different number. Depending on your security settings, a fake get or post submission allows the data to be altered without difficulty. A random token, however, can't be guessed merely by moving to a different integer.

9.13.4. See Also

Recipe 20.9 for more details on verifying data with hashes; documentation on uniqid( ) at http://www.php.net/uniqid and on md5( ) at http://www.php.net/md5.




PHP Cookbook, 2nd Edition
PHP Cookbook: Solutions and Examples for PHP Programmers
ISBN: 0596101015
EAN: 2147483647
Year: 2006
Pages: 445

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net