views:

123

answers:

4

Hi All,

I might not have known what to search for to get this answer so please point me to the correct post if this has been dealt with already.

Now then, I have a little custom CMS and I want to ensure users don't re-submit their $_POST data by refreshing the page. So I've done something like this:

<?
  //Start a session to hold variables I want after my redirect
  session_start();
  if($_POST){ 

    //Do some php stuff and if I'm happy with the results...
    $_SESSION['some_vars'] = $whatever;

    //Bring me back here but without the $_POST data
    header('Location: '.THIS_PAGE);
    exit;

  }
?>

When the script reloads I use my session variables and trash the session.

I'm wondering if anybody has a better way to handle this. It's fairly non cumbersome but I'm always looking for more efficiency.

Thanks.

EDIT: By the way, stackoverflow.com does this somehow when you post a question if what I'm doing seems unclear, but they also make a permalink while they're at it.

A: 

if its important that user dont insert the same data I'm sure there must be some unique column in your database (like headline maybe?). so just check if this headline already exists. you wont need to use any sessions that way

antpaw
@antpaw I like the idea of the browser not asking the person if they want to re-submit the form. Your idea is valid (and yes I'm using a unique ID) but the browser will still nag them about re-submitting. Thanks for the idea.
jeerose
A: 

What about:
1. Generate random string (uniqid or md5)
2. Store it in session and put in into hidden input at the form
3. Check form value and session value - if it matches - process the form. Clear the session value.

habicht
... which is an implementation of anti-CSRF mechanism: http://shiflett.org/articles/cross-site-request-forgeries
Dor
+1  A: 

You have actually implemented what is called the Post-Redirect-Get pattern, and it is absolutely a correct way to do this. I do this myself. I use it so often, I usually implement some minor helper functions into my base controller classes to help me use it:

public function prgRedirect($url = null, $sessionData = null)
{
    if ($sessionData !== null) {
        if (! isset($_SESSION)) session_start();
        $_SESSION['_PRG'] = $sessionData;
    }
    if ($url === null) $url = $_SERVER['HTTP_REFERER'];
    header("Location: ".$url);
}

public function getPrgData()
{
    if (! isset($_SESSION)) session_start();
    if (isset($_SESSION['_PRG'])) {
        $data = $_SESSION['_PRG'];
        unset($_SESSION['_PRG']);
    }
    else {
        $data = null;
    }
    return $data;
}

I usually use it with REST-style URLs, so a POST request will do whatever it has to do, save some data to session using prgRedirect(), which then redirects back to the GET url for the same resource/page. The handler for the GET method will call getPrgData() at the top of the execution and see if there's anything in the session data.

zombat
thanks @zombat. I'm delighted that my brain finally came up with something useful all on it's own! Thanks for pointing out the terminology for me. Do you have a good resource for not just *what* REST is but how to implement it using php?
jeerose
Np. It's always nice to get "industry validation" for something you've implemented :) I don't have any great REST resources handy, but you'll find various overviews on Google. The basics are that each URL is supposed to represent a unique "resource" of your site, and various HTTP methods interact differently with that resource. e.g. http://x.com/car/1 would represent a car with id 1, and a GET request would fetch data, whereas a POST to the same URL would alter the data. It's more of a design strategy than anything, often used with API servers.
zombat
Do you implement that kind of URL using mod_rewrites? How does the GET request interpret that properly in php without the "?"? And what if you had multiple GET variables?
jeerose
zombat
A: 

There are actually two problems here:

  • users hitting the refresh button in their browser when the data is already saved and you are in a page which saves the data
  • user hits the "back" button in the browser and clicks "submit" button once again.

In the first scenario, the best scheme is to follow the GET-after-POST pattern where you use the header("location:somewhere_else.php") call to redirect the user. This way you do not have to worry about being called two times consecutively, since the page where you posted the data, is not in the browser's history list (because the server had returned 302 header).

The second scenario hurts a bit more, because the GET-after-POST does not help. If the user submits the form twice, the you may save the data twice. In this case, there may be several solutions:

  • put a "form identifier" (a random string) in every form you send to the client. When client submits the form, check if you already have such identifier in session data. If you don't have it, save form data and remember the identifier in user's session as "already used". If you find the identifier in session data, don't save anything - it's a duplicate.
  • check the database for exactly the same values that are submitted. If they match, do not save a duplicate. However, the user may have clicked "back" button, changed some piece of data and then re-submitted the form.
naivists
Excellent point with the second scenario, thanks!
jeerose