Jump to content

Post form resubmits when going back in browser history


PHPSuperNewb

Recommended Posts

Hello everyone,

I am having a problem since some time now and need some help.

I have created a login page where the user has to input a username and password to login.

The username will be put in a session and when the user logs out the session data and session itself gets destroyed.

However when I go back in the browser history to the page where I logged in I get the "famous" resend information dialog that asks you to resend the information from the login form. Which means that all the post data gets resend and the user logs in again without having to put in a username and password.

 

Here is my code:

Login.tpl:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
    <head>
        
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
        <title>Snitch</title>
        <link rel="stylesheet" type="text/css" href="templates/css/snitch1440x900.css" />
    </head>
    <body>
    <div id="login-achtergrond">
        <div id="login">
            <form action="." id="loginform" name="login"  method="post">
            <input type="hidden" name="actie" value="Login"></input>
            <input type="text" id="username" name="username" value="" style="opacity:0.7;filter:alpha(opacity=70)"></input>
            <input type="password" id="password" name="password" value="" style="opacity:0.7;filter:alpha(opacity=70)"></input>
            <input id="aanmelden" type="submit" name="submit" value="" style="opacity:0;filter:alpha(opacity=0)">
            </form>
        </div>
        <div id="registreer">
        </div>
    </div>
    </body>    
</html>
    

 

Here is the code of my login page (I created this in a OOP way):

<?php
  class Handler_Login extends Actie_Handler
  {
      function __construct($actie_handle)
      {
        parent::construct($actie_handle);
        $this->actie = $actie_handle;
      }
       function secured_handler()
       {
           if ($this->session->check_session() == false)
           {
                  $password = $_POST['password'];
                  $username = $_POST['username'];
                  $login = $this->dbh->Login($username, $password);
                  if ($login == true)
                  {
                      $this->session->set('username', $username);
                      $this->view->displayHome();
                      $this->view->display(); 
                  }
                  else
                  {
                      echo "You are not logged in!";
                  }
                  unset($_POST['password']);
                  unset($_POST['username']);
               
           }
           if ($this->session->check_session() == true)
           {
               $this->view->displayHome();
               $this->view->display();
           }
       }
  }
?>

 

Here is the code of my logout:

<?php
    class Handler_Loguit extends Actie_Handler
  {
      function __construct($actie_handle)
      {
        parent::construct($actie_handle);
        $this->actie = $actie_handle;
      }
       function secured_handler()
       {
           $this->session->stopSession();
           $this->view->displayLogin();
           $this->view->display();
       }
  }
?>

 

Here is the code of my session:

<?php
    class Session
    {
         function __construct()
         {
             if(!isset($_SESSION))
             {
                session_start();
             } 
         }
         function set($name, $value)
         {
             $_SESSION[$name] = $value;
         }
         
         function get($name)
         {
             return $_SESSION[$name];
         }
    
         function stopSession()
         {
             $_SESSION = array();
             //even though I don't use any cookies someone told me that I had to remove the cookie of the session to completely destroy it?
             //please tell me if this is correct  
             if (isset($_COOKIE[session_name()])) 
             { 
                setcookie(session_name(), '', time()-42000, '/'); 
             }            
             session_destroy(); 
         }
         
         function session_message($naam)
         {
            return print_r($_SESSION[$naam]); 
         }
         
         
         function check_session()
         {
             if(isset($_SESSION['username']) && !empty($_SESSION['username'])) 
             { 
                return true;
             }
             else
             {
                return false;
             }         
        }
    }
?>

 

this is the code of my view for those who might be interested:

<?php
      class view_manager
      {
        private $tpl;
        function __construct()
        {
             
        }
        
        function displayStatus()
        {
            $status = file_get_contents("templates/status.tpl");
            $this->tpl = str_replace("%content%", $status, $this->tpl);
        }
        
        function displayLogin()
        {
            $this->tpl = file_get_contents("templates/login.tpl");
        }
        
        function displayHome()
        {
            $this->tpl = file_get_contents("templates/home.tpl");
        }
        
        function display()
        {   
            echo $this->tpl;
        }
      }
?>

 

using a header to redirect to the login page is not going to work since I use my view_manager to display the pages.

Does anyone know of any solution to get rid of that stupid resend information dialog without using a header? I tryed unsetting the values of POST in my login code but that did not seem to work. Please help me out I've been looking for an answer for over 1 and a half week so far :(

 

Is there anyone who knows how to remove the POST data from a form when the user goes back in browser history?

If it's not possible, is there any solution except using a header?

Link to comment
Share on other sites

You can't, because the back button recreates the previous request with all it's header information. Even redirecting to another URL won't prevent clicking back a few times and re-submitting the POST data - just the way it works. In terms of security there's no great threat, after all the user did enter that information in the first place. What you should do is try to remove their need to hit the back button, by redirecting them to the page they were at before (if possible without a login) or giving them options of where to go.

 

Edit

 

You could also add in some logic that would void that login attempt, so even if they did re-submit the data they wouldn't be logged in.

Link to comment
Share on other sites

so.. Isn't there a way to remove all the header information? (I've seen this before but can't remember where) Because I still see this as a security threat. Imagine: you are going outside and logged out of the website. Your little brother goes on your computer and goes back in history to see what you did. All of a sudden he is on your account OH NOES! :o

 

There should be a solution, I've seen tons of sites who got a good looking login which works the way I want it to :S...

Link to comment
Share on other sites

There's no way for a website to modify the user's history; that in itself would be a security threat. You may not have seen my edit, but you could always add in some logic that would void the re-submission login attempt.

Link to comment
Share on other sites

There's no way for a website to modify the user's history; that in itself would be a security threat. You may not have seen my edit, but you could always add in some logic that would void the re-submission login attempt.

 

Hmm that sounds interesting but I can't quite figure out how I would do that 0.0 sorry  :-\

 

edit:

How would I check if the user submitted the information through form or through the previous header information?

Link to comment
Share on other sites

if the form is posted and the user logs in, set a session variable say $_SESSION['logged'] = true.

then if form gets resent, first thing it does is check for that variable, and if so, skip logging user in.

Although I think you are focusing too much on something that really isnt a big deal.

Link to comment
Share on other sites

When the user logs out, store the current session_id and then regenerate it but retain that piece of data; that means you'll want to manually remove the other session data instead of using session_destroy to flatten it. Within the login code, check if this session 'session ID' variable has been set and compare it to the current session_id. As the cookie will hold the value of the new session ID, but the session variable will contain the old one; you just need to check if they're equal OR the session 'session ID' variable isn't set, for a valid login.

 

I can't actually test this right now, but in theory it should work. Ha.. It would be a lot easier if you could just redirect them at the point of login though.

Link to comment
Share on other sites

if the form is posted and the user logs in, set a session variable say $_SESSION['logged'] = true.

then if form gets resent, first thing it does is check for that variable, and if so, skip logging user in.

Although I think you are focusing too much on something that really isnt a big deal.

 

check my login page and my check_session, it does exactly that. The problem here is that whenever it has logged out the user should be redirected to the login page which is impossible because that would create an infinite loop of redirecting in the login page.

 

When the user logs out, store the current session_id() and then regenerate it but retain that piece of data; that means you'll want to manually remove the other session data instead of using session_destroy() to flatten it. Within the login code, check if this session 'session ID' variable has been set and compare it to the current session_id(). As the cookie will hold the value of the new session ID, but the session variable will contain the old one; you just need to check if they're equal OR the session 'session ID' variable isn't set, for a valid login.

 

I can't actually test this right now, but in theory it should work. Ha.. It would be a lot easier if you could just redirect them at the point of login though.

 

Thank you, I haven't taken a look into session ID's, I'll try to figure out how they work and get back to you :) Hopefully this works, because I'm out of ideas :D

Eeeerm would I have to store the session_id in a cookie? because the session kinda gets destroyed when the user logs out :S?

EDIT:

Ah nevermind I see what you did there! This sounds good :D

Link to comment
Share on other sites

Ok, I'm trying to do this right but I still don't 100% understand session_id's here is my code, please don't laugh xD

How would I do this the correct way? I think something is wrong in this condition in my session: if(!isset($_SESSION['ID_old']) || $_SESSION['ID_old'] == $_SESSION['ID_new']) I'm not sure though...

 

Login:

<?php
  class Handler_Login extends Actie_Handler
  {
      function __construct($actie_handle)
      {
        parent::construct($actie_handle);
        $this->actie = $actie_handle;
      }
       function secured_handler()
       {
           if ($this->session->check_session() == false)
           {
               $ID = $this->session->check_session_id(); 
               if ($ID == false)
               {
                   echo "false";
                  $password = $_POST['password'];
                  $username = $_POST['username'];
                  $login = $this->dbh->Login($username, $password);
                  if ($login == true)
                  {
                      $this->session->set('username', $username);
                      $this->view->displayHome();
                      $this->view->display(); 
                  }
                  else
                  {
                      echo "you are not logged in";
                  }          
               }
               else
               {
                   echo "true"; 
                   $this->view->displayLogin();
                   $this->view->display();
               }
                 
           if ($this->session->check_session() == true)
           {
               $this->view->displayHome();
               $this->view->display();
           }
       }
     }
  }
?>

 

session:

<?php
    class Session
    {
         function __construct()
         {
             if(!isset($_SESSION))
             {
                session_start();
             } 
         }
         function set($name, $value)
         {
             $_SESSION[$name] = $value;
         }
         
         function get($name)
         {
             return $_SESSION[$name];
         }
    
         function stopSession()
         {
             unset($_SESSION['username']);
             $_SESSION['ID_old'] = session_id();
             echo "<pre>";
             print_r($_SESSION['ID_old']);
             echo "</pre>";
             session_regenerate_id();
             $_SESSION['ID_new'] = session_id();
             echo "<pre>";
             print_r($_SESSION['ID_new']);
             echo "</pre>";   
         }
         
         function session_message($name)
         {
            return print_r($_SESSION[$name]); 
         }
         
         
         function check_session()
         {
             if(isset($_SESSION['username']) && !empty($_SESSION['username'])) 
             { 
                return true;
             }
             else
             {
                return false;
             }         
        }
        
        function check_session_id()
        {
             if(!isset($_SESSION['ID_old']) || $_SESSION['ID_old'] == $_SESSION['ID_new'])
             {
                 echo "<pre>";
                 print_r($_SESSION['ID_old']);
                 echo "</pre>";
                 print_r($_SESSION['ID_new']);
                 echo "</pre>";
                 return true;
             }
             else
             {
                 echo "<pre>";
                 print_r($_SESSION['ID_old']);
                 echo "</pre>";
                 print_r($_SESSION['ID_new']);
                 echo "</pre>"; 
                 return false;
             }
        }
    }
?>

 

edit: sorry for my double post, I couldn't edit my previous one :S

Link to comment
Share on other sites

In your form action


I don't quite understand the dot, but it seems to me that you could POST to a "random" script.  In other words... throw in some random GET variables in there.. more specifically the time.

 

[code]

That way it will always POST to a new script and the variables can never be reloaded... So long as you code around that technique.  i.e check for $_GET['r']

Link to comment
Share on other sites

This might give you a better understanding why I use the dot in there :)

 

index.php:

<?php
  function __autoload($class_name)
  {
     @include_once 'classes/class.' .$class_name . '.php';
  }
  try 
  {
    
    if(isset($_REQUEST['actie']) && !empty($_REQUEST['actie']))
    {
        $actie = $_REQUEST['actie'];      
    }
    else
    {
        $actie = 'home';
    }
    $disp = new Dispatcher($actie);
    $disp->handle_de_actie();
  }
  catch(Exception $e)
  {
    $error_handler = new Handler_error($e);
    $error_handler->handled_actie();
  }
?>

 

dispatcher:

<?php
    class Dispatcher
    {
        private $handle;
        function __construct($actie_handle)
        {
            $this->handle = $actie_handle;   
        }
        function handle_de_actie()
        {
            $naam = "Handler_{$this->handle}";
            if (class_exists("$naam"))
            {
                $handler_obj = new $naam($this->handle);
                $handler_obj->secured_handler();
            }
            else
            {
                throw new Exception("Can't handle this");
            }
        }
    }
?>

 

actie_handler:

<?php
    abstract class Actie_Handler
    {
        protected $sessie;
        protected $view;
        protected $dbh;
        
        function construct()
        {
            $this->sessie=new Sessie();
            $this->view=new view_beheer();
            $this->dbh=new DatabaseHelper();
        }
    
        abstract function secured_handler();
    }  
?>

 

Everything is written in dutch so some things might not be acurate with my other scripts, don't worry I changed my other scripts for readabillity, however I'm to lasy to do it with these though :P

However this should be pretty straight-forward.

 

 <form action="." id="loginform" name="login"  method="post">            
<input type="hidden" name="actie" value="Login"></input>            
<input type="text" id="username" name="username" value="" style="opacity:0.7;filter:alpha(opacity=70)"></input>            
<input type="password" id="password" name="password" value="" style="opacity:0.7;filter:alpha(opacity=70)"></input>            
<input id="aanmelden" type="submit" name="submit" value="" style="opacity:0;filter:alpha(opacity=0)">            
</form>

 

This basically puts out this url: ?actie=Login&username="someuserinput"&password="someuserinput"

which then calls my class.Handler_Login.php

 

Also.. I don't quite understand what you have posted. How can time affect this? I really don't understand it, could you please elaborate further. I'd love to hear a solution ^.^

Link to comment
Share on other sites

this basically puts out this url: ?actie=Login&username="someuserinput"&password="someuserinput"

which then calls my class.Handler_Login.php

 

Also.. I don't quite understand what you have posted. How can time affect this? I really don't understand it, could you please elaborate further. I'd love to hear a solution ^.^

Adding microtime to the end of a file...as a querystring will guarentee that it is a new reference every time.  Much like the way a few people setup their stylesheets so others won't have to clear their cache every time a change is made... meaning it's a NEW stylesheet every time.  This may or may not work like I'm thinking for POST reloads, but it's worth a shot to see.

 

One last thing.  Your markup isn't right.  INPUT tags are self-closing.. and even though you didn't "self-close" them all, you forgot to close the last one... the submit button.

Link to comment
Share on other sites

this basically puts out this url: ?actie=Login&username="someuserinput"&password="someuserinput"

which then calls my class.Handler_Login.php

 

Also.. I don't quite understand what you have posted. How can time affect this? I really don't understand it, could you please elaborate further. I'd love to hear a solution ^.^

Adding microtime to the end of a file...as a querystring will guarentee that it is a new reference every time.  Much like the way a few people setup their stylesheets so others won't have to clear their cache every time a change is made... meaning it's a NEW stylesheet every time.  This may or may not work like I'm thinking for POST reloads, but it's worth a shot to see.

 

One last thing.  Your markup isn't right.  INPUT tags are self-closing.. and even though you didn't "self-close" them all, you forgot to close the last one... the submit button.

yes, thanks for the elaboration and pointing that out for my inputs ^.^ allthough I'm quite sure I've already changed that in my code since I just posted a older version of it. :)

I will try to use a post method for the microtime, however I still don't quite get how it works. Do I have to create a condition in my login script of the posted microtime to check if it's a new stylesheet? How would this condition look like?

I am quite new to this so I'll just have to try. I'll be back to you when I think I did it right :P

 

I feel like such a newb right now :(

 

edit: b.t.w. if sending the microtime through post doesn't work would it be safe for me to use $_GET instead for all of my user input data with a .htaccess file? I don't know of any security issues it might cause but maybe someone else does ;)

Link to comment
Share on other sites

Zanus, I think I have understood what you meant with the microtime.

What your saying is that your posting the microtime to the login script when the user logs in. When the user goes back in history to get to the login page again that microtime should still be in the cache which means that the microtime is the same as the one from before right? However it's different when you go to the login without going back in history since it's then posted again instead of still being in the cache. I think I finally understand.

 

I have tested it and came to 1 small problem... When I send it through post and go back in history the post is undefined.

I will test this for $_get but I'm not sure if it's going to work. Could you please show me an example of how you would've done it?

Here is mine:

 

class.Handler_Login.php:

<?php
  class Handler_Login extends Actie_Handler
  {
      function __construct($actie_handle)
      {
        parent::construct($actie_handle);
        $this->actie = $actie_handle;
      }
       function secured_handler()
       {
           if ($this->sessie->check_sessie() == false)
           {
                  $time = $_POST['r'];
                  $previous_time = $this->sessie->get('r');
                  $this->sessie->set('r', $time);
                  if ($previous_time == $time)
                  {
                      $this->view->toonLogin();
                      $this->view->toon();
                  }
                  if ($previous_time == false || $previous_time != $time)
                  {
                       $paswoord = $_POST['paswoord'];
                       $gebruikersnaam = $_POST['gebruikersnaam'];
                       $login = $this->dbh->Login($gebruikersnaam, $paswoord);
                       if ($login == true)
                       {
                           $this->sessie->set('gebruikersnaam', $gebruikersnaam);
                           $this->view->toonHome();
                           $this->view->toon(); 
                       }
                       else
                       {
                           echo "U bent niet ingelogd";
                       }                
                  }
                 
           if ($this->sessie->check_sessie() == true)
           {
               $this->view->toonHome();
               $this->view->toon();
           }
       }
     }
  }
?>

 

class.Sessie.php:

<?php
    class Sessie
    {
         function __construct()
         {
             if(!isset($_SESSION))
             {
                session_start();
             } 
         }
         function set($naam, $waarde)
         {
             $_SESSION[$naam] = $waarde;
         }
         
         function get($name)
         {
             if (isset($_SESSION[$name]))
             {
                return $_SESSION[$name];
             }
             else
             {
                 return false;
             }
         }
    
         function stopSessie()
         {
             unset($_SESSION['gebruikersnaam']);  
         }
         
         function sessie_bericht($naam)
         {
            return print_r($_SESSION[$naam]); 
         }
         
         
         function check_sessie()
         {
             if(isset($_SESSION['gebruikersnaam']) && !empty($_SESSION['gebruikersnaam'])) 
             { 
                return true;
             }
             else
             {
                return false;
             }         
        }
    }
?>

 

Login.tpl:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
    <head>
        
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
        <title>Snitch</title>
        <link rel="stylesheet" type="text/css" href="templates/css/snitch1440x900.css" />
    </head>
    <body>
    <div id="login-achtergrond">
        <div id="login">
            <form action="." id="loginform" name="login"  method="post">
            <input type="hidden" name="actie" value="Login"/>
            <input type="hidden" name="r" value="<?php echo microtime();?>"/>
            <input type="text" id="gebruikersnaam" name="gebruikersnaam" value="" style="opacity:0.7;filter:alpha(opacity=70)"/>
            <input type="paswoord" id="paswoord" name="paswoord" value="" style="opacity:0.7;filter:alpha(opacity=70)"/>
            <input id="aanmelden" type="submit" name="submit" value="" style="opacity:0;filter:alpha(opacity=0)"/>
            </form>
        </div>
        <div id="registreer">
        </div>
    </div>
    </body>    
</html>

 

so.. when I went back in history and pressed the resend button I got an error telling me that r was undefined at class.Handler_Login.php at this line:

$time = $_POST['r'];

 

However, when I use it without going back in history it does work. I think you are right, this should be done through get, I'm going to test it right away.

 

EDIT: sorry for yet another double post :(

Link to comment
Share on other sites

However, when I use it without going back in history it does work. I think you are right, this should be done through get, I'm going to test it right away.

Yes, this technique is meant to be used with GET, otherwise you just have another POST variable hanging out.  Also, $r is undefined because you're not checking it's existence with isset.  If you change you post action to include this GET variable .... it might just work.

Link to comment
Share on other sites

However, when I use it without going back in history it does work. I think you are right, this should be done through get, I'm going to test it right away.

Yes, this technique is meant to be used with GET, otherwise you just have another POST variable hanging out.  Also, $r is undefined because you're not checking it's existence with isset.  If you change you post action to include this GET variable .... it might just work.

 

Is it possible to have get and post methods in 1 form? Because I can not use a second form. I can however make all my data input get and use a .htaccess file for my login which isn't really what I want though :S

I can't quite figure this out :(

 

It feels like I'm so close to solving this problem  :D

Link to comment
Share on other sites

Is it possible to have get and post methods in 1 form?

Yes, if you structure your form like this

</pre>
<form action="'somescript.php?getvariable=something'" method="'post'">

</form

The GET variable is in the form action, although the method is to POST, the GET var will still show up on somescript.php

Link to comment
Share on other sites

Is it possible to have get and post methods in 1 form?

Yes, if you structure your form like this

<form action='somescript.php?getvariable=something' method='post'>
<input type='submit' name='submit' value='Submit' />
</form>

The GET variable is in the form action, although the method is to POST, the GET var will still show up on somescript.php

ok so I had the microtime working for a while since it was echo'ing but now all of a sudden it doesn't show the echo anymore :S all it does right now is say that the variable contains a string called:"<?php echo microtime(); ?>" it doesn't contain the actual microtime for some particulair reason.

I tryed it with get and with post and both didn't show up anything :S

I tryed this:

<form action=".?r=<?php echo microtime();?>" id="loginform" name="login"  method="post">

and this:

<input type="hidden" name="r" value="<?php echo microtime();?>"/>

 

it did work before but for some reason r contains "<?php echo microtime();?>" instead of the actual microtime right now :( any idea?

 

the strange thing is even though I tryed echo'ing $_POST['r']; and $_GET['r']; they both didn't display anything but when I debug my script through a program like nusphere it says that r does contain the string...

Link to comment
Share on other sites

Only reason I can think it would do that is if the page this ran on was not a php file.

 

the form is in a template file .tpl and the login script is in php, strange thing is that it did work before. I copyed over my previous version and it still didnt work :S I checked everything and everything is like the way it was >.<

 

I thought I finally found my solution and then it gets fked up >.>

Link to comment
Share on other sites

Archived

This topic is now archived and is closed to further replies.

×
×
  • Create New...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.