Solved

The same session not the same value when called twice on same page

Posted on 2016-09-03
23
50 Views
Last Modified: 2016-09-03
I have set a session variable and used its value in a hidden field. I then want to check that the session variable is equal to the one that is posted. However, the token I echo out from the session itself and the token in the form (view source in browser to see it) are not the same and therefore the form never submits. I don't understand why the tokens are not equal?

$_SESSION['token'] = md5(uniqid(mt_rand(), true));
$token = $_SESSION['token'];
echo $token;

Open in new window


if (isset($token) && $_POST['token'] === $token) {
// do something

Open in new window


<input type="hidden" class="hide" name="token" id="token" value="<?php echo $token; ?>">

Open in new window

0
Comment
Question by:Black Sulfur
  • 11
  • 7
  • 2
  • +2
23 Comments
 
LVL 31

Expert Comment

by:Marco Gasi
ID: 41782806
Can you post the whole page(s) involved here? The isue might depend on the structure of the code.
Where you're setting the $_SESSION['token'] value? I mean: in which point of the code flow? It looks like a new value for this variable is generated before any other code so the $token value is always different. But to know this I need to see the whole script(s)
0
 

Author Comment

by:Black Sulfur
ID: 41782812
session_start();

$_SESSION['token'] = md5(uniqid(mt_rand(), true));
$token = $_SESSION['token'];
var_dump($token);

$error = "";

if ($_SERVER['REQUEST_METHOD'] == "POST") {

	if (!$_POST['email']) {
		
		$error .= "You must enter an email address";
	}

	if ($_POST['email'] && filter_var($_POST['email'], FILTER_VALIDATE_EMAIL) === false) {

		$error .= "Email invalid";

	}

	if (isset($token) && $_POST['token'] === $token) {

	$email = $_POST['email'];

	$sql = "SELECT userID FROM `users` WHERE email = '".$link->real_escape_string($email)."'";
	$result = $link->query($sql);
	if ($result->num_rows == 1) {

		$validation_code = md5($email + microtime());

		setcookie('temp_access_code', $validation_code, time() + 60);

		$sql = "UPDATE `users` SET identifier = '".$link->real_escape_string($validation_code)."' WHERE email = '".$link->real_escape_string($email)."'";
		$result = $link->query($sql);


		$subject = "Please reset your password";
		$message = "Here is your password reset code {$validation_code}. Click here to reset your password http://mysite.com/blog/recover.php?email=$email&code=$validation_code";


		}
	}
}

Open in new window

0
 
LVL 54

Expert Comment

by:Julian Hansen
ID: 41782819
Probably because you are regenerating it every time you load the page
i.e. you have
$_SESSION['token'] = md5(uniqid(mt_rand(), true));

Open in new window

Instead of
$_SESSION['token'] = empty($_SESSION['token']) ? md5(uniqid(mt_rand(), true)) : $_SESSION['token'];

Open in new window


You can see a working sample here
1
Is Your AD Toolbox Looking More Like a Toybox?

Managing Active Directory can get complicated.  Often, the native tools for managing AD are just not up to the task.  The largest Active Directory installations in the world have relied on one tool to manage their day-to-day administration tasks: Hyena. Start your trial today.

 

Author Comment

by:Black Sulfur
ID: 41782821
Hi Julian, that link doesn't work?
0
 

Author Comment

by:Black Sulfur
ID: 41782826
Check out the screen shot. If I view the source they are the same but just viewing the page normally the output looks different until you right click and view source in browser. It's just weird.
source.jpg
0
 

Author Comment

by:Black Sulfur
ID: 41782827
Hi again Julian, I actually want the code to change every time I refresh the page.
0
 
LVL 29

Expert Comment

by:Olaf Doschke
ID: 41782828
Regardless of the link not working, if you set $_SESSION['token'] = md5(uniqid(mt_rand(), true)); everytime, of course it does not match the  $_POST['token'], as that submits the previous $_SESSION['token'] overridden. Test, if $_SESSION['token'] already is set and don't set it twice for the same session.

Julians "Instead of" code will work that way. You also have to load your $_SESSION via session_start();, otherwise the $_SESSION is unset and you start a new session with every request. Look into the help topic of session_start(): http://php.net/manual/en/function.session-start.php

 it does one of two things: 1 - start the session if it's not started or 2 - load the $_SESSION array or as the manual states it Start new or resume existing session. It is needed before you test set or unset any $_SESSION array element.

Bye, Olaf.
0
 
LVL 31

Expert Comment

by:Marco Gasi
ID: 41782832
That is what I meant: you create everytime a new token before to run other code.
0
 

Author Comment

by:Black Sulfur
ID: 41782834
Man, I am getting confused, lol. Is it possible to make this work but a new token is generated after refreshing the page? And in that case do I even need the token to be in a session variable?
0
 
LVL 54

Expert Comment

by:Julian Hansen
ID: 41782836
Then do the check first and then reset the token after you have done the check.
0
 

Author Comment

by:Black Sulfur
ID: 41782842
I am trying to do this.. I will post back if I mange to get it right or not,  but just struggling  at the minute.
0
 
LVL 54

Expert Comment

by:Julian Hansen
ID: 41782843
Try the link again - it definitely works.

Here is some code to illustrate how to do the check first
<?php
session_start();

  
$message = false;
if ($_POST) {
  $token = isset($_POST['token']) ? $_POST['token'] : false;
  if ($token) {
      if ($token == $_SESSION['token']) {
        $message = "Tokens match";
      }
      else {
        $message = "Tokens do not match: Expected {$_SESSION['token']}, received {$token}";
      }
  }
  else {
    $message = "No token found";
  }
}
// Now you can regenerate the token
$_SESSION['token'] = md5(uniqid(mt_rand(), true);
?>
<!doc

Open in new window

0
 

Author Comment

by:Black Sulfur
ID: 41782844
Cool, will check your code now. Clicking on your link gives me this error:

This site can’t be reached

mrcp9’s server DNS address could not be found.
Search Google for mrcp9 t1535
ERR_NAME_NOT_RESOLVED
0
 
LVL 54

Expert Comment

by:Julian Hansen
ID: 41782846
Updated example here

The above sample implements the code snippet in the previous post.

Press submit on the page to check unmodified token,
Edit HTML with console (F12) to change input value and submit to see error
0
 
LVL 54

Expert Comment

by:Julian Hansen
ID: 41782854
Then right click and Inspect Element - or whatever you need to get into the console.

I have updated the sample again to display the text box rather than as a hidden - concept is the same.
0
 

Author Comment

by:Black Sulfur
ID: 41782860
What does this line do?

$token = isset($_POST['token']) ? $_POST['token'] : false;

Open in new window

0
 

Author Comment

by:Black Sulfur
ID: 41782869
Okay, so I tried to incorporate your code into what I had and it seems to work now but i don't know if it is laid out right. Please could you tell me how to make it better:

$error = "";

if ($_SERVER['REQUEST_METHOD'] == "POST") {
	
	$token = isset($_POST['token']) ? $_POST['token'] : false;
	
	if ($token) {
      if ($token == $_SESSION['token']) {

		if (!$_POST['email']) {
		
		$error .= "You must enter an email address";
	}

	if ($_POST['email'] && filter_var($_POST['email'], FILTER_VALIDATE_EMAIL) === false) {

		$error .= "Email invalid";

	}	
		
	$email = $_POST['email'];

	$sql = "SELECT userID FROM `users` WHERE email = '".$link->real_escape_string($email)."'";
	$result = $link->query($sql);
	if ($result->num_rows == 1) {

		$validation_code = md5($email + microtime());

		setcookie('temp_access_code', $validation_code, time() + 60);

		$sql = "UPDATE `users` SET identifier = '".$link->real_escape_string($validation_code)."' WHERE email = '".$link->real_escape_string($email)."'";
		$result = $link->query($sql);


		$subject = "Please reset your password";
		$message = "Here is your password reset code {$validation_code}. Click here to reset your password http://localhost:8888/simpleblog/code.php?email=$email&code=$validation_code";


		} else {
		
		echo "something went wrong";
		}
	  }
	}
}
$_SESSION['token'] = md5(uniqid(mt_rand(), true));

Open in new window

0
 
LVL 54

Accepted Solution

by:
Julian Hansen earned 500 total points
ID: 41782882
Firstly
$token = isset($_POST['token']) ? $_POST['token'] : false;

Open in new window

Is the same as saying
if (isset($_POST['token'])) {
   $token = $_POST['token'];
}
else {
   $token = false;
}

Open in new window

In other words if the token value was included in the post then capture it else set token to false.

The code looks fine bar one thing - you seem to allow processing to continue if email is invalid - which probably is not right.
As to whether the code is right - you need to decide that in terms of functionality. I have reformatted it a bit - your layout left blocks out of alignment which can lead to errors down the line if you are unable to match opening and closing {}. It also makes it easier to see the flow of the code
$error = "";

if ($_SERVER['REQUEST_METHOD'] == "POST") {
  
  $token = isset($_POST['token']) ? $_POST['token'] : false;
  
  if ($token) {
    if ($token == $_SESSION['token']) {
      if (!$_POST['email']) {
        $error .= "You must enter an email address";
      }
      //**
      // YOU PROBABLY WANT TO EXIT HERE IF EMAIL IS NOT SET
      //** 
      if ($_POST['email'] && filter_var($_POST['email'], FILTER_VALIDATE_EMAIL) === false) {
        $error .= "Email invalid";
      }  
      //**
      // *****  WHAT IF EMAIL IS INVALID DO YOU STILL WANT TO PROCEED?
      //**
      // else { with closing tag after this block
      $email = $_POST['email'];

      $sql = "SELECT userID FROM `users` WHERE email = '".$link->real_escape_string($email)."'";
      $result = $link->query($sql);
      if ($result->num_rows == 1) {
        $validation_code = md5($email + microtime());
        setcookie('temp_access_code', $validation_code, time() + 60);
        $sql = "UPDATE `users` SET identifier = '".$link->real_escape_string($validation_code)."' WHERE email = '".$link->real_escape_string($email)."'";
        $result = $link->query($sql);
        $subject = "Please reset your password";
        $message = "Here is your password reset code {$validation_code}. Click here to reset your password http://localhost:8888/simpleblog/code.php?email=$email&code=$validation_code";
      } 
      else {
         // I ASSUME THIS IS FOR DEBUGGING - YOU PROBABLY DON'T WANT THIS HERE
        echo "something went wrong";
      }
    } // End if token valid
  } // End if token is set
} // end post

$_SESSION['token'] = md5(uniqid(mt_rand(), true));

Open in new window

0
 

Author Comment

by:Black Sulfur
ID: 41782890
Good point, it shouldn't carry on if the email is invalid or empty. Thanks so much for your help. I really appreciate your patience and willingness to help me. I am still really new to this and trying to find my feet. So, thanks again!

I did notice another problem with my code in that the cookie doesn't seem to actually be setting. If I go to Application in google chrome I only see the session and no cookie. Must I ask a related question for that issue?
0
 

Author Comment

by:Black Sulfur
ID: 41782896
Sorry, one last thing about this. You have mentioned closing off if the email address is empty or invalid. How do I actually do that?

/**
      // YOU PROBABLY WANT TO EXIT HERE IF EMAIL IS NOT SET
      //** 
      if ($_POST['email'] && filter_var($_POST['email'], FILTER_VALIDATE_EMAIL) === false) {
        $error .= "Email invalid";
      }  
      //**
      // *****  WHAT IF EMAIL IS INVALID DO YOU STILL WANT TO PROCEED?
      //**

Open in new window

0
 
LVL 54

Expert Comment

by:Julian Hansen
ID: 41782897
Put the rest of the code in an else block.
0
 
LVL 109

Expert Comment

by:Ray Paseur
ID: 41782900
I haven't read this entire dialog, but there may be a couple of links that can help in this article.
https://www.experts-exchange.com/articles/11909/PHP-Sessions-Simpler-Than-You-May-Think.html

Also, if you're thinking of using a "form token" as a security measure, forget about it and choose CAPTCHA instead.  Form tokens were an interesting idea in 2003, and were completely defeated by simple HTML screen scrapers in 2004, so just don't do that.  If you want to know more about this, read the historical writings of Chris Shiflett (but understand that some of this is now ancient history).

PHP has a built-in function that can tell you if a variable exists and has been initialized to some kind of non-falsy value.  Before you set something like $_SESSION['token'] you can use empty($_SESSION['token']) to test the session token value.  If it's already there, ...
1
 
LVL 29

Expert Comment

by:Olaf Doschke
ID: 41782908
Form token is still a good protection against cross site request forgery (CSRF) and listed as top defense for that here: https://www.owasp.org/index.php/Cross-Site_Request_Forgery_(CSRF)_Prevention_Cheat_Sheet before double cookie defense.

It's not good against spam and bots, that's true, but in conjunction with a good defense against session hijacking it ensures the answer is from the same user (or bot) requesting the form. So no, it's not bad to do this, it's not good against spam, but it's an essential good security system to always add a token to a form.

For example see https://www.youtube.com/watch?v=vRBihr41JTo

Bye, Olaf.
1

Featured Post

Is Your AD Toolbox Looking More Like a Toybox?

Managing Active Directory can get complicated.  Often, the native tools for managing AD are just not up to the task.  The largest Active Directory installations in the world have relied on one tool to manage their day-to-day administration tasks: Hyena. Start your trial today.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Suggested Solutions

Title # Comments Views Activity
PHPStorm debugging issues 1 45
Why do people dis php? 5 50
Check for a change in value in a database row on jquery button click 6 33
Cookie not unsetting 7 19
Author Note: Since this E-E article was originally written, years ago, formal testing has come into common use in the world of PHP.  PHPUnit (http://en.wikipedia.org/wiki/PHPUnit) and similar technologies have enjoyed wide adoption, making it possib…
Part of the Global Positioning System A geocode (https://developers.google.com/maps/documentation/geocoding/) is the major subset of a GPS coordinate (http://en.wikipedia.org/wiki/Global_Positioning_System), the other parts being the altitude and t…
Learn how to match and substitute tagged data using PHP regular expressions. Demonstrated on Windows 7, but also applies to other operating systems. Demonstrated technique applies to PHP (all versions) and Firefox, but very similar techniques will w…
The viewer will learn how to create a basic form using some HTML5 and PHP for later processing. Set up your basic HTML file. Open your form tag and set the method and action attributes.: (CODE) Set up your first few inputs one for the name and …

772 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question