I'm having trouble accessing a $_SESSION variable on an external page.
Basically, I have a form on a drupal node, which when submitted, POSTS to an external php file which saves the value to a $_SESSION variable like so:
//Bootstrap Drupal so we can use the SESSION variables, where we store the calling number for the
//duration of the logged in session
define('DRUPAL_ROOT', $_SERVER['DOCUMENT_ROOT']);
$base_url = 'http://'.$_SERVER['HTTP_HOST'];
require_once DRUPAL_ROOT . '/includes/bootstrap.inc';
drupal_bootstrap(DRUPAL_BOOTSTRAP_SESSION);
//Save number as the calling number for the session
$_SESSION['calling-number']=$_REQUEST['calling-number'];
That works fine.
When I later try and access the $_SESSION variable from an external page using:
//Bootstrap Drupal so we can use the SESSION variables, where we store the calling number for the
//duration of the logged in session
define('DRUPAL_ROOT', $_SERVER['DOCUMENT_ROOT']);
$base_url = 'http://'.$_SERVER['HTTP_HOST'];
require_once DRUPAL_ROOT . '/includes/bootstrap.inc';
drupal_bootstrap(DRUPAL_BOOTSTRAP_SESSION);
echo $_SESSION['calling-number'];
I get nothing. If I look at the $user on this new page, I can see that it is because it is running under an anonymous user session (uid = 0), instead of the logged in user ID which set the $_SESSION variable, and thus it can't find it.
Any ideas why I'm not using the session of the logged in user?
EDIT
Not sure why this is, but as long as the two external files are in different directories, this works fine. If they are in the same directory, it seems to start a new session and not access the existing one. No idea why though.
If I understand correctly, you submit a form from Drupal, where there is an authenticated user, to an external PHP file. When you try accessing the session from an external page, which bootstraps Drupal, the session doesn't contain the previously saved value, and the user is reported to be an anonymous user, and not the authenticated user who previously committed the form.
This normally happens because:
Drupal doesn't find the cookies whose names are session_name(), and substr(session_name(), 1).
In this case, drupal_session_initialize() executes the following code.
// Set a session identifier for this request. This is necessary because
// we lazily start sessions at the end of this request, and some
// processes (like drupal_get_token()) needs to know the future
// session ID in advance.
$GLOBALS['lazy_session'] = TRUE;
$user = drupal_anonymous_user();
// Less random sessions (which are much faster to generate) are used for
// anonymous users than are generated in drupal_session_regenerate() when
// a user becomes authenticated.
session_id(drupal_hash_base64(uniqid(mt_rand(), TRUE)));
if ($is_https && variable_get('https', FALSE)) {
$insecure_session_name = substr(session_name(), 1);
$session_id = drupal_hash_base64(uniqid(mt_rand(), TRUE));
$_COOKIE[$insecure_session_name] = $session_id;
}
Drupal doesn't find a record in its sessions database table for a sid (or ssid) equal to the value of the $sid argument passed to its session read callback (_drupal_session_read()). This could also happen if the session has expired.
if ($is_https) {
$user = db_query("SELECT u.*, s.* FROM {users} u INNER JOIN {sessions} s ON u.uid = s.uid WHERE s.ssid = :ssid", array(':ssid' => $sid))->fetchObject();
if (!$user) {
if (isset($_COOKIE[$insecure_session_name])) {
$user = db_query("SELECT u.*, s.* FROM {users} u INNER JOIN {sessions} s ON u.uid = s.uid WHERE s.sid = :sid AND s.uid = 0", array(
':sid' => $_COOKIE[$insecure_session_name],
))
->fetchObject();
}
}
}
else {
$user = db_query("SELECT u.*, s.* FROM {users} u INNER JOIN {sessions} s ON u.uid = s.uid WHERE s.sid = :sid", array(':sid' => $sid))->fetchObject();
}
Drupal always save the session for the current user when PHP shutdowns, through _drupal_session_write(). It is done also when the current user is an anonymous user.
if (!drupal_save_session()) {
// We don't have anything to do if we are not allowed to save the session.
return;
}
// Check whether $_SESSION has been changed in this request.
$last_read = &drupal_static('drupal_session_last_read');
$is_changed = !isset($last_read) || $last_read['sid'] != $sid || $last_read['value'] !== $value;
// For performance reasons, do not update the sessions table, unless
// $_SESSION has changed or more than 180 has passed since the last update.
if ($is_changed || !isset($user->timestamp) || REQUEST_TIME - $user->timestamp > variable_get('session_write_interval', 180)) {
// Either ssid or sid or both will be added from $key below.
$fields = array(
'uid' => $user->uid,
'cache' => isset($user->cache) ? $user->cache : 0,
'hostname' => ip_address(),
'session' => $value,
'timestamp' => REQUEST_TIME,
);
// Use the session ID as 'sid' and an empty string as 'ssid' by default.
// _drupal_session_read() does not allow empty strings so that's a safe
// default.
$key = array(
'sid' => $sid,
'ssid' => '',
);
// ...
db_merge('sessions')
->key($key)
->fields($fields)
->execute();
}
For those who have the same problem when using an external page:
You just have to put some more parameters to the code
In fact, you have to precise your drupal directory.
Ex. for a page which is in the directory before your drupal directory
// Definine the path to "DRUPAL_ROOT" which is used throughout bootstrap.inc
define('DRUPAL_ROOT', getcwd() . '/your_drupal_directory');
// you could write like this too
// define('DRUPAL_ROOT', $_SERVER['DOCUMENT_ROOT'] . '/your_drupal_directory');
// Define the $base_url which will be used by Drupal to make sure we are on the same domain
$base_url = 'http://'.$_SERVER['HTTP_HOST']. '/your_drupal_directory';
require_once DRUPAL_ROOT . '/includes/bootstrap.inc';
drupal_bootstrap(DRUPAL_BOOTSTRAP_SESSION);
echo $_SESSION['calling-number'];
Related
On my website, I allow users to view a users information by simply clicking their name. Once they click the persons name, they can schedule the person to come to an event. When the user clicks "schedule me" I take the them full name from the "user_id" and send it as a "$_SESSION['speaker']" to the next file that pretty much checks if the user came from the last file and takes the name and uses it as the input value for the calendar. The problem I am having is that when the user didn't "click schedule" from the other file and goes to the calendar website alone, the name from the previous person they clicked stays there and I want it to be blank in case they want to put a different name. So pretty much i would access the calendar website just by typing the URL and the name would still be in the session. I want to clear the session without logging the user out so they don't see the name of the previous person they clicked. Here is some of my code
First file
$_GET['speaker'] = $_SESSION['speaker_id'];
$speaker_id = $_GET['speaker'];
$stmtSpeaker = $handler->prepare("SELECT * FROM formdata WHERE user_id= :speaker_id");
$stmtSpeaker->bindParam(':speaker_id', $speaker_id, PDO::PARAM_INT);
$stmtSpeaker->execute();
$formData = $stmtSpeaker->fetch();
if(isset($_POST['schedule_me'])){
$_SESSION['admin'] = $adminBoolean;
$_SESSION['speaker'] = $formData['fullname'];
$_SESSION['speaker_came'] = true;
header("Location: admincalendar.php");
exit;
}
Second file
$adminBoolean = $resultChecker['admin'];
if($_SESSION['speaker_came'] = true){
$speaker = $_SESSION['speaker'];
}else{
$speaker = "";
}
Unset will destroy a particular session variable whereas session_destroy() will destroy all the session data for that user.
It really depends on your application as to which one you should use. Just keep the above in mind.
unset($_SESSION['name']); // will delete just the name data
session_destroy(); // will delete ALL data associated with that user.
You can unset session variable
$adminBoolean = $resultChecker['admin'];
if($_SESSION['speaker_came'] = true){
$speaker = $_SESSION['speaker'];
}else{
unset($_SESSION['speaker']);
unset($_SESSION['speaker_came']);
$speaker = '';
}
You need to first get the tempkey of the element and then unset it. Try this:
if(($tempkey = array_search($speaker_id, $_SESSION['speaker'])) !== FALSE)
unset($_SESSION['speaker'][$tempkey]);
I've got a problem with a SESSION, inside a function.
How can I set $_SESSION['idUser'] = $result['idUser']; to a SESSION, which I can use for upcoming activity's.
<?php
// Session starts
session_start();
class DB_Functions {
public function getUserByEmailAndPassword($email, $password) {
$result = mysql_query("SELECT * FROM User WHERE emailadress = '" . $email . "'") or die(mysql_error());
// check for result
$no_of_rows = mysql_num_rows($result);
if ($no_of_rows > 0) {
$result = mysql_fetch_array($result);
//This is the session I want to use
$_SESSION['idUser'] = $result['idUser'];
return $result;
Assuming this is just a snippet of a larger class, I see no reason why this line:
$_SESSION['idUser'] = $result['idUser'];
wouldn't work (again assuming that $result['idUser'] contains a value). Ensure that you have session_start(); called on all pages that you want to use the global session variable.
I suggest using the following in the first place:
Session Management Library
I highly recommend it.
Like you wanted, you could use it everywhere.
It'll keep you from using super globals like $_SESSION (not easy to debug)
Make your code more testable and it'll alleviate you from having to manage sessions manually.
Example code:
use Symfony\Component\HttpFoundation\Session\Session;
$session = new Session();
$session->start();
// set and get session attributes
$session->set('name', $result['id_user']);
$session->get('name'); //Easy to get 'id_user' in another page.
//Other examples:
// set flash messages (appears only once, for warnings/alerts)
$session->getFlashBag()->add('notice', 'Profile updated');
// retrieve messages
foreach ($session->getFlashBag()->get('notice', array()) as $message)
{
echo '<div class="flash-notice">'.$message.'</div>';
}
If you like like what you see here are the links:
Download
Install Instructions
Warning
Make sure your PHP session isn't already started before using the
Session class. If you have a legacy session system that starts your
session, see Legacy Sessions.
Delete the space before <?php. Before session_start() there shouldn't be any output.
Here is the first question and I need your help.
I transfer form data from first page using header location method in php to second page.
On the second page I accept the data using get.
Now here the url of 2nd page, after the data is sent (i.e. form is submitted)
http://mydomain.com/site1/form1_conf.php?id=123
When user is on second page, the data on second page is being displayed according the id number from the mysql database.
Now the problem is that when the user is on second page and he changes the number (for ex. 123 to say 78) the data of id=78, from the database is displayed, which is no good.
How can I stop that?
Please Note: I can't use post, nor can I use sessions.
EDITE:
php code on first page, to transfer to second page:
// after all validations are okay
$insert = //insert into database
$result = mysql_query($insert);
if($result)
{
echo("<br>Input data is succeed");
$lastInsertedId = mysql_insert_id();
header('Location:form1_conf.php?id='.$lastInsertedId); //THIS IS THE IMPORTANT LINE
}
else
{
$message = "The data cannot be inserted.";
$message .= "<br />" . mysql_error();
}
Your problem is not with the URLs: to a power user changing cookies or POST-variables is as trivial as editing GET-variables for a regular user. You'll need some way to 'sign' the requests as being valid.
Easiest to do this is with a "pre-shared key", which you use with one-way hashes to validate requests.
Redirector:
$newURL = '/newpage?id='.$id.'&hash='.sha1('mypresharedkey'.$id);
header('HTTP/1.1 303 See other');
header('Location: '.$newURL);
die;
The other page:
$idToShow = $_GET['id'];
$hash = sha1('mypresharedkey'.$id);
if($hash != $_GET['hash'])
die("Tssss, don't play with the address bar!");
else
RenderThePage();
This ensures end users can only access pages they've been allowed to by the submit.
For your specific code:
...all prior code
$lastInsertedId = mysql_insert_id();
$timestamp = time();
header('Location:form1_conf.php?'.http_build_query([
'id' => $lastInsertedId,
'time' => $timestamp,
'hash' => sha1('some-generated-key'.$timestamp.$lastInsertedId)
]);
In the other page, including a timebomb if you want (otherwise just comment it out):
$id = $_GET['id'];
$time = $_GET['time'];
if($_GET['hash'] != sha1('some-generated-key'.$time.$id))
die('URL was tampered with');
if(time() - $time > 300)
die('URL was only valid for 5 minutes');
You need to track the user and the id that they have in your database to make sure that they haven't changed the number. So when you get the information via the GET you make sure that it is legit.
Users can change the id or even attempt to go directly to that page via the url. So you need some sort of server-side check to verify that it is ok.
You could complicate this "cheating" a bit, if you didn't pass the ID number directly, but somehow encrypted it.
Let's say, you define a salt:
define(SALT, 'long weird salt with special characters etc.');
Here comes the first part you want:
$lastInsertedId = mysql_insert_id();
$querytag = base64_encode($lastInsertedId); // just to make it less readable
$checksum = md5($querytag . SALT); // and make a hash
header('Location:form1_conf.php?id=' . $querytag . '&checksum=' . $checksum);
At the beginning of form1_conf.php, you put this:
$encodedId = $_GET['id'];
$oldChecksum = $_GET['checksum'];
$newChecksum = md5($encodedId . SALT);
$id = base64_decode($encodedId);
if($newChecksum != $oldChecksum) {
die('You Are Cheating!');
}
... do something with the $id ...
The point is that since you add SALT to the hash, some user can't simply use md5 on a changed ID, because he's missing the SALT you used.
It'd be even better if the salt wasn't the same every time.
You should never trust the url because there is always a way to manipulate the data.
So you should do validation after retreiving the data. If the result does not fit you: for example the loggedin user with the ID = 1 requests the settings page from the userid = 3 you do not show the result.
<?php
$userID = $_GET['id'];
if($userID != $expectedResult)
{
//show errormessage, redirect or show the page with the users data
}
?>
Working on a site that displays different content based on a cookie value. For example:
http://peewee.betaforming.com/
vs.
http://peewee.betaforming.com/?cu=10010
That value can be set on any page because I have a functions include on every page. If the cookie is set or already saved, the information for that CU is loaded. If no cookie value is set or a value is passed that doesn't exist in the DB, he site displays default information.
Here's the problem. If you go from no cookie value set to requesting the site with "?cu=10010" attached to any page, the current page doesn't load the current data until it is refreshed.
From what I've read, I need to refresh the page using header("location.... but I'm not sure where I do that given all that I need to do based on that cookie value.
Here's the relevant code in the functions file for setting/retrieving the cookie.
// CU cookies
if (isset($_GET["cu"]) && is_numeric($_GET["cu"])) {
$pass_cu = $_GET["cu"];
// See if passed value returns an active CU record
mysql_select_db($database_peewee, $peewee);
$query_rs_valid_cu = "SELECT * FROM tbl_cus WHERE cu_id = $pass_cu";
$rs_valid_cu = mysql_query($query_rs_valid_cu, $peewee) or die(mysql_error());
$row_rs_valid_cu = mysql_fetch_assoc($rs_valid_cu);
$totalRows_rs_valid_cu = mysql_num_rows($rs_valid_cu);
if ($totalRows_rs_valid_cu != 0) {
// Set cookie
$peewee_cu_querystring = $_GET["cu"];
$expire_month = time()+60*60*24*30; //30 days
//kill current cookie
setcookie("peewee_cu", "", time()-10);
//set new cookie
setcookie("peewee_cu", $peewee_cu_querystring, $expire_month, "/");
}
mysql_free_result($rs_valid_cu);
}
// See of cookie exists
if ((isset($_COOKIE['peewee_cu'])) && $_COOKIE['peewee_cu'] != "") {
$cu_cookie_value = $_COOKIE['peewee_cu'];
// Set values for getting CU record
$colname_rs_cu_data = $cu_cookie_value;
$load_custom_cu = 'true';
} else {
// Set defualt CU value
$colname_rs_cu_data = 10000;
$load_custom_cu = 'false';
}
// Get and Set CU Information (CU specific or default)
mysql_select_db($database_peewee, $peewee);
$query_rs_cu_data = "SELECT * FROM tbl_cus WHERE cu_id = $colname_rs_cu_data";
$rs_cu_data = mysql_query($query_rs_cu_data, $peewee) or die(mysql_error());
$row_rs_cu_data = mysql_fetch_assoc($rs_cu_data);
$totalRows_rs_cu_data = mysql_num_rows($rs_cu_data);
$cu_sidebar_image = $row_rs_cu_data['cu_logo'];
$cu_sidebar_name = $row_rs_cu_data['cu_name'];
$cu_sidebar_link = $row_rs_cu_data['cu_link'];
$cu_sidebar_address = $row_rs_cu_data['cu_address'];
$cu_sidebar_city = $row_rs_cu_data['cu_city'];
$cu_sidebar_state = $row_rs_cu_data['cu_state'];
$cu_sidebar_postal = $row_rs_cu_data['cu_postal'];
$cu_sidebar_phone = $row_rs_cu_data['cu_phone'];
$cu_sidebar_toll = $row_rs_cu_data['cu_phone_toll_free'];
$cu_meta_title = $row_rs_cu_data['cu_name'];
$cu_tab_title = $row_rs_cu_data['cu_name'];
mysql_free_result($rs_cu_data);
// Set default error page for all pages except home page
$default_error_page = 10007;
$default_error_page_home = 10005;
Thanks
Brett
Reloading the page just to read in a cookie whose value you know (because you've just set it) seems a bit redundant.
Instead, all you need to do is set a variable to either the current cookie value as sent by the browser ($_COOKIE['peewee_cu']) or the value you're assigning to that cookie on the current page ($peewee_cu_querystring).
For a really simple way (but note: I don't particularly recommend writing to superglobals, it's better to have your own variable and manage scope properly) see PHP sets COOKIE, changed with JQUERY COOKIE plugin, cannot be edited with php?
Incidentally, you shouldn't need to kill off the old cookie before setting the new one, as any new cookie with the same name, domain, and path will overwrite it automatically.
I have the function as follows:
$sid = mysql_real_escape_string( $_COOKIE['session_id'] );
if( $sid ) { session_id( $sid ); }
// Start session
if( !session_start() ) { die( 'Session could not be started.' ); }
$sid = session_id();
// Validate session id
$user = $this->validateSessionId( $sid );
if( $user ) {
if( !$user['uid'] ) {
trigger_error( '`Services->__construct()` - Could not find user by session id \''.(string)$sid.'\'.',
E_USER_ERROR );
}
$_SESSION['uid'] = $user['uid'];
$_SESSION['user_name'] = $user['name'];
$_SESSION['user_email'] = $user['email'];
$_SESSION['user_created'] = $user['created'];
}
// If no valid session id, user is anonymous
else {
$_SESSION['user_name'] = 'Anonymous';
$_SESSION['user_created'] = time();
}
When a user first logs in, a cookie is created for them called session_id with the id of their current session. This id is also added to a database table. My understanding is that the session eventually expires on both the client and the server, but a cookie and a database entry can be set to last as long as I'd want.
validateSessionId checks a table of session ids in the database. If the session is found, and hasn't last been accessed more than 30 days ago (it's removed from the table if so), an associative array from the user list representing the user associated with that session id. That user's data is then loaded into the current session.
Am I understanding the workings of this correctly?
The whole session_id() business can be avoided. PHP will automatically check for the presence of a session cookie when you call session_start(), and retrieve the session's ID at that time. You're just duplicating what's already done for you.
Your code presumes that the cookie PHP is sending out to track the session is actually a permanent cookie. Usually PHP sends out a session cookie only (e.g. delete when browser is closed). As such, the user will not have ANY session cookie when they come back to the site, and will get a brand new session each time.