2

I'm struggling to understand an answer on SO. It's a solution which prevents the form from being processed twice (if someone clicks "submit" button twice in a row).

It generates a unique token and stores it in the form. So if the submit button is clicked twice it will ignore the duplicate submission.

Code is

    // form.php
    <?php
        // obviously this can be anything you want, as long as it is unique
        $_SESSION['token'] = md5(session_id() . time());
    ?>
    <form action="foo.php" method="post">
        <input type="hidden" name="token" value="<?php echo $_SESSION['token'] ?>" />
        <input type="text" name="bar" />
        <input type="submit" value="Save" />
    </form>

    // foo.php
    if (isset($_SESSION['token']))
    {
        if (isset($_POST['token']))
        {
            if ($_POST['token'] != $_SESSION['token'])
            {
                // double submit
            }
        }
    }

Everyone agrees that it's the right solution, but I don't understand why the $_SESSION['token'] changes the second time we click the submit button.

Thank you for your help

Robert Sinclair
  • 4,550
  • 2
  • 44
  • 46
  • 1
    *"but I don't understand why the $_SESSION['token'] would be different the second time we click the submit button."* - That's the whole reason behind it ;-) – Funk Forty Niner Jul 30 '18 at 17:39
  • but submit refreshes the page which in turn refreshes the generated token in the header, no? – Robert Sinclair Jul 30 '18 at 17:40
  • 1
    Every time you load the page you're overwriting the token stored in the session here `$_SESSION['token'] = md5(session_id() . time());` – Jay Blanchard Jul 30 '18 at 17:41
  • 2
    The part you seem to be missing is that if the `$_POST['token'] === $_SESSION['token']` you change the token in the session so that if the form was submitted twice, the same token is posted twice, changed on the first submit and doesn't match on the second. – Jonathan Jul 30 '18 at 17:41
  • 1
    depends on what the action is for it, but yeah; it'll do that. If you want to keep the same session array, assign it somewhere and use it later on. Kind of hard to understand what this is really all about. – Funk Forty Niner Jul 30 '18 at 17:41
  • 1
    To prevent timing attack, do not use `==` use `hash_equals()` – Rotimi Jul 30 '18 at 17:43
  • 1
    And when someone "double clicks" a submit button, it submits the same form data more than once. It doesn't refresh the page from the first click and click a second time. This is to prevent something like double click submitting the same data, not preventing someone from submitting the form more than once. – Jonathan Jul 30 '18 at 17:43
  • @AkintundeOlawale This isn't a password, a timing attack doesn't matter here. And that only helps if you know the un-hashed string which they don't have (usually submitted as the password in a form). They are storing the final hash and that is what is submitted. Also, the client already has the hashed string (it's in a form field), so why would you use a timing attack to try to guess the hash. – Jonathan Jul 30 '18 at 17:45
  • changed on the first submit and doesn't match on the second. <- this is the part i'm not understanding, why does it not match on the second submit? I'm probably missing some kind of a basic understanding of what happens when we click the submit button – Robert Sinclair Jul 30 '18 at 18:11
  • I just printed out both SESSION and POST and I can see that when we click the submit button twice, the POST token stays the same but the SESSION token changes. – Robert Sinclair Jul 30 '18 at 18:23
  • 1
    @RobertSinclair Took me a while to find this, but this will work for you https://www.phpro.org/tutorials/Preventing-Multiple-Submits.html if you want. I used that years ago and couldn't remember the domain name for it. Seeing it now; yep, that's it alright! Give that a whirl. – Funk Forty Niner Jul 30 '18 at 18:30
  • 1
    You're welcome. I only saw your comment above now Robert. Remember to use `@me` ;-) just in case I might have been dragged away somewhere against my own free will LOL! *Cheers* – Funk Forty Niner Jul 30 '18 at 18:32
  • 1
    @FunkFortyNiner thank you for the tip and I think I figured out what's happening based on the link you provided. I'll write an answer based on that. Hopefully my understanding of it was correct :) – Robert Sinclair Jul 30 '18 at 18:36
  • Ok @RobertSinclair and you're welcome. I'll take a look at it. Glad to see you figured it out. *Cheers!* – Funk Forty Niner Jul 30 '18 at 18:37
  • 1
    I'm just hoping that this hasn't anything to do with not starting the session everywhere for all pages using sessions. @RobertSinclair – Funk Forty Niner Jul 30 '18 at 18:39
  • @FunkFortyNiner oh no, it's not that for sure :) Please take a look at my answer, do you think it makes sense? Thanks again! – Robert Sinclair Jul 30 '18 at 18:41
  • 1
    That makes sense @RobertSinclair ;-) Glad I was of help and to (hopefully) have made your day. – Funk Forty Niner Jul 30 '18 at 18:43
  • 1
    @FunkFortyNiner definitely made my day, thank you! – Robert Sinclair Jul 30 '18 at 18:58
  • will this work for GET as well ? – Jason Mar 27 '21 at 09:44
  • The original link @FunkFortyNiner posted is now dead, just dropping this here if anyone wants to view it: https://web.archive.org/web/20160318130130/https://www.phpro.org/tutorials/Preventing-Multiple-Submits.html – Zippy Feb 15 '23 at 16:16

1 Answers1

4

Based on the supplied link by @FunkFortyNiner I think I figured it out, hopefully I understood this correctly.

What happens: If we click the submit button twice the $_POST token will remain the same but the $_SESSION token (defined in the header) will change..

From the link: here

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.

When we click submit twice PHP will regenerate the token twice (server side), but the form itself remains the same (client side hasn't regenerated the HTML markup of the form).

In other words PHP regenerates the token with each "submit" request, but your browser doesn't regenerate the form that contains the token.

Robert Sinclair
  • 4,550
  • 2
  • 44
  • 46