?
Solved

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

Posted on 2016-09-03
23
Medium Priority
?
62 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
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 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
 
LVL 1

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 58

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
WordPress Tutorial 4: Recommended Plugins

Now that you have WordPress installed, understand the interface, and know how to install new parts, let’s take a look at our recommended plugins.

 
LVL 1

Author Comment

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

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
 
LVL 1

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 30

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
 
LVL 1

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 58

Expert Comment

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

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 58

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
 
LVL 1

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 58

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 58

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
 
LVL 1

Author Comment

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

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

Open in new window

0
 
LVL 1

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 58

Accepted Solution

by:
Julian Hansen earned 2000 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
 
LVL 1

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
 
LVL 1

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 58

Expert Comment

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

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 30

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

WordPress Tutorial 2: Terminology

An important part of learning any new piece of software is understanding the terminology it uses. Thankfully WordPress uses fairly simple names for everything that make it easy to start using the software.

Question has a verified solution.

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

Nothing in an HTTP request can be trusted, including HTTP headers and form data.  A form token is a tool that can be used to guard against request forgeries (CSRF).  This article shows an improved approach to form tokens, making it more difficult to…
Introduction This article is intended for those who are new to PHP error handling (https://www.experts-exchange.com/articles/11769/And-by-the-way-I-am-New-to-PHP.html).  It addresses one of the most common problems that plague beginning PHP develop…
Explain concepts important to validation of email addresses with regular expressions. Applies to most languages/tools that uses regular expressions. Consider email address RFCs: Look at HTML5 form input element (with type=email) regex pattern: T…
The viewer will learn how to count occurrences of each item in an array.
Suggested Courses

765 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