PHP Forms
Guarding Against Multiple Submissions of the Same Form
Problem
You want to prevent a user from submitting the same form more than once.
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 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='<?= md5(uniqid()) ?>'/>
<input type='submit' value='Save Data' id='submit-button'/>
</form>
Example 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.";
}
}
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 rehit 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 mistake and can slow down the malicious user. It won’t, however, eliminate all fraudulent use: more complicated work is required for that such as adding a CAPTCHA or other verification question to the form.
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.
No comments:
Post a Comment