Porto

Preventing Multiple Submits

Preventing Multiple Submits

The problem of multiple form submissions is quite common in PHP. Many people use forms for database interactions and emailing etc, but when the form is submitted, the page can be refreshed and the POST data, or file uploads is POSTed once again. This tutorial brings an easy remedy to the issue with the use of form tokens and sessions.

By setting a form token within a hidden field in the HTML form, the value can be checked in the page the form data is submitted to. The form token itself is simply a randomly generated string which is added to the form and stored in a session also. In this example the PHP function uniqid() is used to generate the form token value.

The PHP part of the form.php page will look like this.


<?php
        
/*** begin the session ***/
        
session_start();

        
/*** create the form token ***/
        
$form_token uniqid();

        
/*** add the form token to the session ***/
        
$_SESSION['form_token'] = $form_token;
?>

The comments in the code describe the process acuratly, that a session is started, a unique form token is generated, and that value is added to the global PHP session array.

The form token is again used in the HTML form in a hidden field, named form_token, otherwise, the form is just a standard HTML form. The completed form.php file will look like this.


<?php
        
/*** begin the session ***/
        
session_start();

        
/*** create the form token ***/
        
$form_token uniqid();

        
/*** add the form token to the session ***/
        
$_SESSION['form_token'] = $form_token;
?>
<!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>
<title>My Form</title>
</head>
<body>

<form action="submit.php" method="post">
<dl>
<dt>Name</dt>
<dd>
<input type="hidden" name="form_token" value="<?php echo $form_token?>" />
<input type="text" name="first_name" />
</dd>
</dl>
<p><input type="submit" value="Add Name" /></p>
</form>

</body>
</html>

The form action in the above file points to submit.php. This is the file that will process the form data. It could contain code to send emails, or code to perform some functions on a database. For the purposes of this tutorial, it is a simple process of saying thank you to the user and displaying the name.

Like any method of accepting data from users, three rules are followed.

Never Trust User Input

Never Trust User Input

Never Trust User Input

I hope you get the point. Minimum checks for security is to check for type and length. The validation and checking in the submit.php file begins by checking that all the variables that are expected, are in fact set. This includes the first name field, the form token, and the session form token.

The setting of a form token has a secondary security function. Because PHP sessions are stored server side, a check can be made against the POSTed form token and the form token which is stored on the server. This ensures that the form being POSTed is, in fact, the correct form and not a third party form. This means it is our form. The check is a simple string comparison.

The final check is the minimal checking for type and length of the posted first name. With all the checks complete, the POSTed form data is sanitized and assigned to a variable. The variable can then be used in whatever fashion for which it was designated.

Finally, the form token stored in the session is deleted, or unset, with the use of the use of the PHP unset() function. With the session form token deleted, any further form posting will result in a failure to meet the required matching of either being set, or matching the POSTed token. The submit.php file will look like this.


<?php
        
/*** begin the session ***/
        
session_start();

        
/*** check all expected variables are set ***/
        
if(!isset($_POST['first_name'], $_POST['form_token'], $_SESSION['form_token']))
        {
                
$message 'Invalid Submission';
        }
        
/*** check the form tokens match ***/
        
elseif($_POST['form_token'] != $_SESSION['form_token'])
        {
                
$message 'Access denied';
        }
        
/*** check the input name is a string between 1 and 50 characters ***/
        
elseif(strlen(trim($_POST['first_name'])) == || strlen(trim($_POST['first_name'])) > 50)
        {
                
$message 'Invalid First Name';
        }
        else
        {
                
/*** sanitize the input ***/
                
$first_name filter_var($_POST['first_name'], FILTER_SANITIZE_STRING);

                
/*** assign the input ***/
                
$message 'Thank you ' $first_name;

                
/*** unset the form token in the session ***/
                
unset( $_SESSION['form_token']);
        }

?>

<!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>
<title>My Form</title>
</head>
<body>

<h1>Submit Page</h1>
<p><?php echo $message?></p>
</body>
</html>