12

I came up with a technique to prevent duplicate form submission by going back/forward or refreshing the page. And I thought about duscussing it here, I already tested a sample not in production environment, what is flaws that you can identify?

Please note that I am well aware of using Form Tokens, which will defend you against CSRF attacks, and wasn't added in the steps below.

-Generate Form ID for each form, and use it as hidden field in the form:

$formid = microtime(true)*10000;

-On form submit:

  • Validate from data

  • Calculate the hash of form fields data

    $allvals = '';
    foreach($_POST as $k=>$v){
        $allvals .= $v;
    }
    $formHash = sha1($allvals);
    
  • Validate form hash by comparing with previously saved hashes. the session value is binded to each form by $formid variable.

    $allowAction = true;
    if(isset($_SESSION['formHash'][$_POST['formid']]) && ($_SESSION['formHash'][$_POST['formid']] == $formHash)){
         $allowAction = false;
    }
    
  • if form hash wasn't found, it means this is the first time form submitted or the form data is changed.
  • If data saved ( to database, for example), save form hash to session:

    $_SESSION['formHash'][$_POST['formid']] = $formHash;
    

Full version of the code: http://thebusy.me/2011/01/06/preventing-duplicate-form-submissions/

isogashii
  • 181
  • 1
  • 2
  • 7
  • Consider benchmarking your hash snippet against `$formHash = sha1(serialize($_POST));`. It'll hash keys too, which may or may not be beneficial. – Dan Lugg Jan 06 '11 at 14:18
  • Possibly related/dup, http://stackoverflow.com/questions/4614052/how-to-prevent-multiple-form-submission-on-multiple-clicks-in-php/4614310#4614310 – user562374 Jan 06 '11 at 14:37
  • Possible duplicate of http://stackoverflow.com/questions/218907/how-to-handle-multiple-submissions-server-side – Jake McGraw Jan 06 '11 at 15:43

3 Answers3

7

A simpler way to achieve what you want is to use redirect on submit. After you process a POST request you redirect, possibly even to the same page. This is a common pattern called "Redirect after POST" or POST/Redirect/GET.

For example:

<?php
if($_POST) {
    // do something

    // now redirect
    header("Location: " . $_SERVER["REQUEST_URI"]);
    exit;
}
?>

<html> ...
<form method="post" action=""> ... </form>

By setting the action to "" then it will submit to itself, at which point the if($_POST) code block will validate to true and process the form, then redirect back to itself.

Of course you probably want to redirect to a different page that shows a "your form has been submitted" response or put the form on a different page and have the HTML of this page be the response.

The benefit of this method is that when you hit the back button it does a GET request so the form is not re-submitted.

On Firefox, it will actually take the submission to itself out of the browser history so when users browse across the web and then hit back, instead of seeing the "thank you" page they see the form page.

newz2000
  • 2,602
  • 1
  • 23
  • 31
  • yes, I read about this, and I will use it for future projects. the issue is for my current websites, i am using form tokens, and I don't want to change the structure of code by redirecting to other pages. Thank you @newz2000 – isogashii Jan 06 '11 at 22:11
  • 1
    you could even go one step further to enable a thank you message. Setup 2 divs, one with the form and one with the thank you message. Then set the thank you div to display:none in css. Have the redirect go back to formpage.php?thankyou=1 if ( $_GET['thankyou'] ==1){css to make the form div display none, and set the thank you to display:block} – MSD Jan 12 '11 at 20:50
  • 5
    This needs to be combined with form tokens to be foolproof. If the user refreshes or re-submits while the POST request is in progress, you end up with a duplicate request. – bcoughlan May 23 '12 at 00:20
  • And your answer is not a solution though every programmer do redirection – Aitazaz Khan Mar 05 '16 at 04:34
0

It looks like you are getting overly complicated with this. My favorite way, because it also prevents some session jacking hacks at the same time, is described here:

http://www.spotlesswebdesign.com/blog.php?id=11

It's simple and easy to impliment on any form. It uses a randomly generated page instance id to verify that the form submission received is identical to the last page served to that particular user.

dqhendricks
  • 19,030
  • 11
  • 50
  • 83
  • 2
    thank you for the answer, but, the post states: "It is important to have your form processing logic occur before you set the new session page instance id for this to work, although there are ways around this." Try open two pages at the same time, submitting the first one won't work, becuse new page id is generated when the second page opened, that's the weakness of this technique. But you can use array of generated page ids, and check against them, and remove the one that match from the array. – isogashii Jan 14 '11 at 10:41
  • @isogashii I have modified the article to reflect the array-based method. – dqhendricks Jan 14 '11 at 20:23
  • 1
    unfortunately the link is dead – Fanax Dec 28 '16 at 14:31
-1

Both solutions above are good but a bit short.

how about stopping further insertions in the next few minutes from the same user with perhaps minor changes in data?

this can be done by putting an md5 hash in a cookie on the users machine and storing a copy in the database - this way any further attempt from the same machine over a specified time can be ignored and stopped from being inserted into the database.

perhaps someone can comment on the validity and effectiveness of my suggestion or am i barking up the wrong tree ???

ali
  • 9
  • 2