Link to home
Start Free TrialLog in
Avatar of sasnaktiv
sasnaktivFlag for United States of America

asked on

Browser's Back Button is robbing us!

Hi Guys,
I have a real problem and it may not be an easy one to solve.
I hope I can explain this clearly.

I have a number of PHP pages which perform the following tasks. They are all within an iFrame.
Step 1) Page_1.php Takes the customers payment then presents Page_2.php.
Step 2) Page_2.php Provides a Paid Receipt and a link to Page_3.php
Step 3) Page_3.php Presents the customer with the merchandise then sends the customer to Page_4.php
Step 4) Page_4.php Summarizes the sale & provides delivery information then refreshes to the LogIn.php page.

Now here's the problem.
If the customer is on Page_4.php or even after it refreshes to LogIn.php, and clicks the Browser's BACK Button (not to be confused with a javascript history() button), the browser (and it does not matter which browser) sends the customer back to Page_3.php with all of its $variables intact.
That is a BIG TIME DISASTER!
Because on Page_3.php the customer then can get the merchandise again without paying for it the second time!
This allows the customer to get/steal FREE merchandise each time the customer clicks the Browser's BACK Button. The customer can do this all day long!

How can this be prevented?
I know this is a tough challenge to solve, but I'd appreciate all the help I can get on this.
Sas
Avatar of Dave Baldwin
Dave Baldwin
Flag of United States of America image

Tthe Browser's BACK Button and the javascript history() are exactly the same thing,  There is Only one history list.  When go 'back' by either method, the browser displays the last image of the previous page without going back to the server... unless the page was loaded thru a POST method.  Then it will check with the server.  

In the shopping carts I maintain, I use PHP Sessions and on the last page, I delete the session after the purchase has been completed.  Also all the pages use POST to go to the next page.  Then if someone clicks on 'Back' on the last, the browser reloads the prevous page except that since the session data is gone, the order data is no longer there.
on page3 use
location.replace("/path/to/page4.html");

more info on this page (see You can't change history section an d others):
http://www.hunlock.com/blogs/Mastering_The_Back_Button_With_Javascript

but look like your application have bad design, you should not trust any operation logic on the client side
You need to change the design.  A quick fix is to simply open up a new browser instance without the address bar and back button.   The better solution is to incorporate a unique Id in every page that can only be used once

Lots of solutions online to prevent back buttons as well as page refreshes from having undesired results
Avatar of sasnaktiv

ASKER

Okay Dave,
That makes sense but I must pass the data as a "GET" method as follows:
<meta http-equiv='refresh' content='0;url=Page_3.php?LotsOfVariables=$LotsOfVariables' method='GET' />

So how can I use  the POST method and the <meta 'refresh' content=0
???

I've been able to pass the data using the  "location.replace("/path/to/page4.php??LotsOfVariables=$LotsOfVariables");"  suggested by "leakim971" but the Browser's Back Button still brings me to Page_3.php

Sas
Sas
You can NOT use POST with <meta 'refresh'.  All simple links are GETs.  If you are stuck with this poor design, @dlethe's suggestion of a unique id is the way to go.  You can also use Sessions with GET methods but POST is always going to be better because it can not be edited in the address bar.

Virtually all decent shopping carts use Sessions.  http://us1.php.net/manual/en/book.session.php
Please see: http://iconoun.com/demo/prevent_multi_submit.php
<?php // prevent_multi_submit.php
error_reporting(E_ALL);

/**
 * PREVENT REPEATED DATA SUBMISSIONS DUE TO BROWSER REFRESH
 * RE-CLICK ON SUBMIT, OR BROWSER BACK BUTTON
 *
 * GET-METHOD REQUESTS SHOULD BE IDEMPOTENT AND (IN THEORY)
 * MUST NOT DISRUPT THE DATA MODEL.  THIS FUNCTION CAN TEST
 * EITHER $_GET OR $_POST REQUEST VARIABLES
 *
 * EXAMPLE:
 *    if ( multi_submit() )
 *    {
 *       handle error
 *    }
 *    else
 *    {
 *       normal processing
 *    }
 */


// ALWAYS START THE PHP SESSION ON EVERY PAGE
session_start();


// A FUNCTION TO RETURN TRUE OR FALSE ABOUT MULTI-SUBMIT CONDITIONS
function multi_submit($type="POST")
{
    // MAKE THE FUNCTION WORK FOR EITHER GET OR POST SUBMITS
    $input_array = (strtoupper($type) == "GET") ? $_GET : $_POST;

    // GATHER THE CONTENTS OF THE SUBMITTED FIELDS AND MAKE A MESSAGE DIGEST
    $string = NULL;
    foreach ($input_array as $val)
    {
        $string .= $val;
    }
    $string = md5($string);

    // IF THE SESSION VARIABLE IS NOT SET THIS IS NOT A MULTI-SUBMIT
    if (!isset($_SESSION["multi_submit"]))
    {
        $_SESSION['multi_submit'] = $string;
        return FALSE;
    }

    // IF THE SESSION DATA MATCHES THE MESSAGE DIGEST THIS IS A MULTI-SUBMIT
    if ($_SESSION['multi_submit'] == $string)
    {
        return TRUE;
    }
    else
    {
        $_SESSION['multi_submit'] = $string;
        return FALSE;
    }
}


// SHOW HOW TO USE THE FUNCTION
if (!empty($_POST))
{
    if (multi_submit())
    {
        echo "ALREADY GOT THAT";
    }
}


// CREATE THE FORM FOR THE DEMONSTRATION
$form = <<<FORM
<form method="post">
ENTER SOMETHING, THEN REENTER IT
<input name="mydata" />
<input type="submit" />
</form>
FORM;

echo $form;

Open in new window

Please let me know if you have any questions.
No luck so far Guys.
Based on Mr. Baldwin's input I've rewritten much of the code to use POST instead of the GET method, but the Browser's Back Button still brings back Page_3.php with all its values intact.
I'm trying to make Ray's invention work in my environment but having a bit of a time doing so. For one thing I'm not so sure it should be added to Page_3.php. I need to kick this around some more.

Maybe there's a way on Page_3.php to detect if the customer used the Browser's Back Button to get to it.
And if so, retrieve a $variable which I can use to disable/enable the SUBMIT button that delivers the merchandise.

Any ideas on those lines?
Sas
I can't dwell on this too much tonight, but there is no need to load all those different pages.  A single PHP script can create several different HTML documents, depending on the contents of the request and the existing values in the database.  You may want to think about redesigning the application with that in mind.
Maybe there's a way on Page_3.php to detect if the customer used the Browser's Back Button to get to it.
No, there isn't.  The 'Back' function is strictly in the browser.  It reloads the last image of the previous page from cache if it is available.  If Page_3.php was loaded thru POST, then you should at least get the message asking if you really want to do that.
I'll see if I have time to come up with a demo that works.  Maybe tomorrow.
SOLUTION
Avatar of Dave Baldwin
Dave Baldwin
Flag of United States of America image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
As I stated earlier, two tried and true methods are to launch a new window (presumably after they log in), where this new window removes the buttons (the refresh can get you as well, along with code that can access the history).  Even seeing the address bar can be used for an exploit.  The other is to incorporate a USE-ONCE ID field , but that is more work.

I urge you to just prevent the problem by opening up a new session where you control 100% of the environment.  You're talking only a few minutes and problem solved.

P.S. you ARE using SSL, right???
Hi Guys,
First of all I want to thank you all for the attention and input.

I've been kicking around everyone's suggestion that I could understand.

My last one was to apply Dave Baldwin's 4 files.
Here's what happens:
1) PostForm.html begins the input and submits it
2) addvalue.php captures the $variables and sends them to
3) postmid.php displays the $variables and presents a SUBMIT button aimed at
4) postend.php which merely displays the $variables
That's all well & good.
When I click the Browser's Back Button I am returned to postmid.php which nolonger has the values.
That's fabulous!
But when I click the Browser's Back Button again ----- I find myself with PostForm.html and all of the $variables still intact, and ready to be SUBMITTED again.
That's not so fabulous!

Is there a way to disable the SUBMIT button in  PostForm.html once it has been clicked?
Thanks,
Sas


Also, dlethe suggests that I open a new session. But I thought I was doing that since every document starts with "session_start();"   Is that not the case?
"session_start();" only starts a new session if there is no current session.  It continues the current session if one exists.  It is necessary to use "session_start();" on each page to give you access to the $_SESSION variables.

The example I gave you destroys the current session on the last page so the $_SESSION variables are no longer there.  When you click on Back, the previous page starts a new session since at that point there is no current session.  Going back a second time has never been a problem so I didn't give it any thought.
ASKER CERTIFIED SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Why not just do the quick fix by launching a NEW window after the login that has no address bar and no buttons?

Thanks Dlethe,
Nice approach, but how do I that?
Sas