Search code examples
phpsessionsession-hijacking

Form token does not match when user refreshes page


I wrote the following three functions in an attempt to prevent session Hijacking. They work. I call set_auth_token at the beginning of the script. Then in the html form I call get_auth_token. After the form has been posted I call check_auth_token. However, sometimes after a user has filled in a form, and they fill it in incorrectly, they tend to press F5/Refresh. This causes the page to 'die'. It does not happen before a form has been filled in.

What can I do to improve these, so that they do not die each time a user presses refresh?

function set_auth_token()
{
   if (!isset($_SESSION['auth_token']))
   {
      $_SESSION['auth_token'] = hash('sha256',rand().time().$_SERVER['HTTP_USER_AGENT'].$_SERVER['REQUEST_URI']);
   }
}

get_auth_token is always called after set_auth_token has been called, hence it dies if it cannot find an auth token:

function get_auth_token()
{
   if (isset($_SESSION['auth_token']))
   {
      return $_SESSION['auth_token'];   
   }
   else
   {
      die('"No auth token."');
   }
}

The function that causes the page to die:

function check_auth_token()
{
   if (array_key_exists('auth_token', $_SESSION) && array_key_exists('auth_token', $_POST))
   {
      if ($_SESSION['auth_token'] == $_POST['auth_token'])
      {
         $_SESSION['auth_token'] = hash('sha256', rand() . time() . $_SERVER['HTTP_USER_AGENT'] . $_SERVER['REQUEST_URI']);
      }
      else
      {
         die('Woah! It seems like you pressed Refresh (or Back/Forward). Just click here to sort it out.'); # TODO: hyperlink silly
      }
   }
   else
   {
      die('no auth token');
   }
}

Cheers.


Solution

  • My guess (based on your comment) is that when the user refreshes, they are sending their POST data again, but the auth_token is being updated at the beginning of the script, and hence the value in the $_POST (generated on previous page) will no longer equal the new value (generated at the beginning of the script).

    You should look at Post/Redirect/Get to stop the resending of Post data.

    You may also consider not regenerating the key until you have validated or invalidated the existing one.