Solved

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

Posted on 2016-09-03
23
35 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 30

Expert Comment

by:Marco Gasi
Comment Utility
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
Comment Utility
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 51

Expert Comment

by:Julian Hansen
Comment Utility
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
 

Author Comment

by:Black Sulfur
Comment Utility
Hi Julian, that link doesn't work?
0
 

Author Comment

by:Black Sulfur
Comment Utility
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
Comment Utility
Hi again Julian, I actually want the code to change every time I refresh the page.
0
 
LVL 29

Expert Comment

by:Olaf Doschke
Comment Utility
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 30

Expert Comment

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

Author Comment

by:Black Sulfur
Comment Utility
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 51

Expert Comment

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

Author Comment

by:Black Sulfur
Comment Utility
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
Free Trending Threat Insights Every Day

Enhance your security with threat intelligence from the web. Get trending threat insights on hackers, exploits, and suspicious IP addresses delivered to your inbox with our free Cyber Daily.

 
LVL 51

Expert Comment

by:Julian Hansen
Comment Utility
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
Comment Utility
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 51

Expert Comment

by:Julian Hansen
Comment Utility
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 51

Expert Comment

by:Julian Hansen
Comment Utility
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
Comment Utility
What does this line do?

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

Open in new window

0
 

Author Comment

by:Black Sulfur
Comment Utility
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 51

Accepted Solution

by:
Julian Hansen earned 500 total points
Comment Utility
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
Comment Utility
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
Comment Utility
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 51

Expert Comment

by:Julian Hansen
Comment Utility
Put the rest of the code in an else block.
0
 
LVL 108

Expert Comment

by:Ray Paseur
Comment Utility
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
Comment Utility
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

IT, Stop Being Called Into Every Meeting

Highfive is so simple that setting up every meeting room takes just minutes and every employee will be able to start or join a call from any room with ease. Never be called into a meeting just to get it started again. This is how video conferencing should work!

Join & Write a Comment

Suggested Solutions

Title # Comments Views Activity
PHP Variable into a number 3 34
Build JSON from table records 17 39
PHP string issue 5 17
wordpress issue 2 15
Generating table dynamically is the most common issue faced by php developers.... So it seems there is a need of an article that explains the basic concept of generating tables dynamically. It just requires a basic knowledge of html and little maths…
Since pre-biblical times, humans have sought ways to keep secrets, and share the secrets selectively.  This article explores the ways PHP can be used to hide and encrypt information.
The viewer will learn how to dynamically set the form action using jQuery.
This tutorial will teach you the core code needed to finalize the addition of a watermark to your image. The viewer will use a small PHP class to learn and create a watermark.

743 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

Need Help in Real-Time?

Connect with top rated Experts

15 Experts available now in Live!

Get 1:1 Help Now