How to tell if a session is active? [duplicate] - php

This question already has answers here:
Check if PHP session has already started
(27 answers)
Closed 1 year ago.
Per request, there are a few different ways that you can tell whether or not a session has been started, such as:
$isSessionActive = (session_id() != "");
Or:
$isSessionActive = defined('SID');
However, these both fail if you start a session, then close it; session_id() will return the prior session's ID, while SID will be defined. Likewise, calling session_start() at this point will generate an E_NOTICE if you already have a session active. Is there a sane way to check if a session is currently active, without having to resort to output buffering, the shut-up operator (#session_start()), or something else equally as hacky?
EDIT: I wrote a patch to try to get this functionality included in PHP: http://bugs.php.net/bug.php?id=52982
EDIT 8/29/2011: New function added to PHP 5.4 to fix this: "Expose session status via new function, session_status"
// as of 8/29/2011
$isSessionActive = (session_status() == PHP_SESSION_ACTIVE);
EDIT 12/5/11: session_status() on the PHP manual.

See edits to the original question; basically, PHP 5.4 and above now has a function called session_status() to solve this problem!
"Expose session status via new function, session_status" (SVN Revision 315745)
If you need this functionality in a pre-PHP 5.4 version, see hakre's answer.

I worked around this by adding a couple wrapper functions around the various session creation/closing/destroying functions. Basically:
function open_session() {
session_start();
$_SESSION['is_open'] = TRUE;
}
function close_session() {
session_write_close();
$_SESSION['is_open'] = FALSE;
}
function destroy_session() {
session_destroy();
$_SESSION['is_open'] = FALSE;
}
function session_is_open() {
return($_SESSION['is_open']);
}
Hackish, but accomplished what I needed.

I'm running into this as well, and a setting in $_SESSION is not an option for me. For PHP 5.3.8:
If any session has been started with the request, define('SID') will return FALSE, as well as $_SESSION is unset.
This is independent whether or not session_id() has been used to set a session id or not.
After the first session_start(), SID is defined and $_SESSION is set to an empty array.
session_destroy() does unset the session_id(), it's an empty string then. SID will remain defined (and set to it's previous value which might be an empty string). $_SESSION is left unchanged. It will get reset/populated next time session_start is called.
With these states, especially as session_id() can be called in between to set the id for the next session, it's not possible to safely determine the session status with SID, $_SESSION and session_id().
"Trying" with session_start() (e.g. with #) might not be really helpful, as this will change the session status and changing the contents of $_SESSION (and adding a set-cookie header if the cookie was not part of the request). It was not fitting in my case.
While I was running tests, I came across the behaviour, that you can not try to change the ini setting of session.serialize_handler when the session is active, not even when you set it to the same value. Same is true for session.use_trans_sidDocs which is more lightweight. This lead me to the following function:
/**
* #return bool
*/
function session_is_active()
{
$setting = 'session.use_trans_sid';
$current = ini_get($setting);
if (FALSE === $current)
{
throw new UnexpectedValueException(sprintf('Setting %s does not exists.', $setting));
}
$result = #ini_set($setting, $current);
return $result !== $current;
}
As far as I can see, the error is checking for active session status only (not disabled), so this should not return a false positive when sessions are disabled.
To get this function compatible with PHP 5.2, it needs a little modification:
/**
* #return bool
*/
function session_is_active()
{
$setting = 'session.use_trans_sid';
$current = ini_get($setting);
if (FALSE === $current)
{
throw new UnexpectedValueException(sprintf('Setting %s does not exists.', $setting));
}
$testate = "mix$current$current";
$old = #ini_set($setting, $testate);
$peek = #ini_set($setting, $current);
$result = $peek === $current || $peek === FALSE;
return $result;
}
Some sandbox.

The following code only dumps one session_id() for me, not two
session_start();
echo session_id();
session_destroy();
echo session_id();
If you're having difficulties with this still you can try creating a variable to check, that you destroy when you destroy the session.
session_start();
$_SESSION['intialized'] = 'This will not print';
$_SESSION = array(); // Unset all variables
session_destroy();
echo $_SESSION['initialized']; // No output

Here's a good drop in replacement that won't break stuff when you move to 5.4:
if(!function_exists('session_status')){
function session_active(){
return defined('SID');
}
}else{
function session_active(){
return (session_status() == PHP_SESSION_ACTIVE);
}
}

Ken, if the session is destroyed, the $_SESSION array should not be available...or at the very least, your values should be unset. So, in theory (untested) you should be able to check the array for a value to know if anything is currently set.

There are multiple places you need to check to verify the session is active:
1- cookie exists and is not expired
2- underlying session storage mechanism (file system or database) has a record matching the cookie.

Related

Read PHP Session without actually starting it

I am looking for a way to read the contents of a PHP session without actually replacing the running session of the current request.
Lets say I have a running session (started before with session_start(), using the default session handler and the session_id given by the PHPSESSID cookie)
Now I have another (valid) session_id and I want to read some of the data stored in that session.
The only way I can think of is to replace the current session with $oldSession = session_id(), set the new session with session_id($newId), then read the data from $_SESSION and then restart the original session with session_id($oldSession) - probably involving some aditional calls of session_commit() and session_start().
But I prefer to not touch the current, running session at all...
PHP 5.4 introduces the SessionHandler class (http://php.net/manual/de/class.sessionhandler.php) but manually calling $SessionHandler->read doesn't seem to work either
$handler = new SessionHandler();
var_dump($handler->read($session));
//Fatal error: SessionHandler::read(): Cannot call default session handler
I am using the native php memcached session handler
Thank you for any tipps!
I solved the problem by directly reading the session data from memcached.
Code based on a comment from http://php.net/manual/de/memcached.sessions.php
$session = 'session_id_to_read';
$servers = explode(',', ini_get('session.save_path'));
$c = count($servers);
for ($i = 0; $i < $c; ++$i) {
$servers[$i] = explode(':', $servers[$i]);
}
$memcached = new \Memcached();
$memcached->addServers($servers);
$sessionPrefix = ini_get('memcached.sess_prefix');
$rawData = $memcached->get($sessionPrefix.$session);
$data = $rawData ? unserialize($rawData) : false;
note that unserialize($rawData) only works if session.serialize_handler is set to 'php_serialize'

URL variable to Session variable (using GET method)

I am trying to store a value obtained from a URL variable into a SESSION variable.
Here is the URL:
Ace Hardware
Here is the SESSION coding, which retrieves the variable, but loses the variable value upon leaving the page.
$_SESSION["store"] = $_GET["store"];
$shopByStore = $_SESSION["store"];
If I plug in the value in quotes as it is below (see "Ace" in code below), it works. But it doesn't work in the code above using the GET method ($_GET["store"])
$_SESSION["store"] = "Ace";
$shopByStore = $_SESSION["store"];
The problem is that you override the $_SESSION["store"] with the $_GET["store"] each time whether the get request exists or not, so it basically only uses $_GET["store"]. You could use this instead:
if (isset($_GET["store"])) {
$_SESSION["store"] = $_GET["store"];
}
At first you need to start the session and then make sure it's already not stored in the session (if it's passed in the $_GET) and if not already saved in the session then store it:
// The first line in your script
session_start();
if (isset($_GET["store"])) {
if (!isset($_SESSION["store"])) {
$_SESSION["store"] = $_GET["store"];
}
}
Read more on PHP manual.

Max depth of Session-array

I have a very intersting problem, and I wonder if there's someone who has a good solution for me:
I'm logging every possible error in a log-file.
Including exceptions, fatal errors, warnings, notices, JS-errors (sent via Ajax),...
Now, for debugging-purposes, I wanted to display all these errors on the HTML-page too (so I don't need to check the log-file).
For this reason, I'm storing every new error inside the Session-Variable. And when the User saw it, it's beeing removed from the Session.
My Problem:
The php-backtrace of an error / exceptions contains lots of information. Including global Variables like $_SESSION.
And since I store all the information in the session, the session-size doubles after each error. (Each error-message in the session contains the whole content of the session before this error)
For example:
No error:
$_SESSION = array();
First error:
$_SESSION = array(error1=>array("msg"="foo", session=array()));
Second Error:
$_SESSION = array(error1=>array("msg"="foo", session=array()), error2 => array("msg"="foo2", session = array(error1=>array("msg"="foo", session=array()))));
(this is only a simplified example)
After 5 Errors, the Session is already so big, that I can't load it from my local server anymore (timeout).
I thought about cutting of every element from an array after the depth of 5 or sth. like that.
Or is it possible to prevent php from storing global variables in the backtrace?
In the moment, I don't store the trace in the Session, but maybe someone knows a better solution, where I can still see basic trace-info
If I may understand you correctly, what you're trying to do is to have some sort of per-session error log, which can be read later on.
My suggestion is to create separate log file per session, you can identify the log file by using the session id, which is unique per session.
<?php
define('PER_SESSION_LOG_PATH', '/tmp/logs/'); //or whatever path
/* return log handle */
class SessionLogger {
static $fh;
static function log_path() {
return PER_SESSION_LOG_PATH . session_id() . '.log';
}
static function log_open() {
if (!self::$fh) {
self::$fh = fopen(self::log_path(), 'a+');
}
return self::$fh;
}
static function add_log($errors) {
if (self::log_open()) {
fwrite(self::$fh, serialize($errors) . PHP_EOL);
}
}
static function log_close() {
if (self::$fh) {
fclose(self::$fh);
}
}
//note that if the size of the log file is larger than memory_limit, you
//may run into problems.
//returns all the logs
static function get_logs($truncate = TRUE) {
$logs = array();
if (self::log_open()) {
while (!feof(self::$fh)) {
$logs[] = unserialize(fgets(self::$fh));
}
//if the logs should be clear, then...
if ($truncate) {
fclose(self::$fh); //close and nullify handle
self::$fh = null;
unlink(self::log_path()); //then delete log file.
}
}
return $logs;
}
}
Then you can add errors by doing
SessionLogger::add_log($error);
You can also read the logs by doing
SessionLogger::get_logs(true); //or false if you don't want to delete file.

Session Share Across Multiple Domains On Same Server

I heard the best method to share session across multiple domains on same server is to use custom php session handler. (ie, domain name different like abc.com, xyz.com but single application.)
But after i tried it, even custom php session handler that using SAME DATABASE ON 1 SERVER can't share session, when i tried to read cookie value from different domain.
Here's my custom session handler, Please kindly check or fix if something missing here. because i've tried it for a week now. can't get it to work
P.S. To get previous session id, i use link such as: newdomain.com/?ssid=[SESSION_ID]
SESSION_INCLUDE.PHP
<?php
// config
$m_host = "localhost"; //MySQL Host
$m_user = "db_user"; //MySQL User
$m_pass = "db_pass"; //MySQL Pass
$m_db = "db_name"; //MySQL Database
$table = "sess_data";
$session_expire = 600; // Session expire time, in seconds (minutes * 60 = seconds)
$gc_probability = 100; // Probability that the garbage collection function will be called. 50% chance by default
ini_set("session.gc_probability",$gc_probability);
/* Open function; Opens/starts session
Opens a connection to the database and stays open until specifically closed
This function is called first and with each page load */
function open ($s,$n) // do not modify function parameters
{
global $session_connection, $m_host, $m_user, $m_pass, $m_db;
$session_connection = mysql_pconnect($m_host,$m_user,$m_pass);
mysql_select_db($m_db,$session_connection);
return true;
}
/* Read function; downloads data from repository to current session
Queries the mysql database, unencrypts data, and returns it.
This function is called after 'open' with each page load. */
function read ($id) // do not modify function parameters
{
global $session_connection,$session_read,$table;
$query = "SELECT data FROM `$table` WHERE id=\"{$id}\"";
$res = mysql_query($query,$session_connection);
if(mysql_num_rows($res) != 1) return ""; // must return string, not 'false'
else
{
$session_read = mysql_fetch_assoc($res);
$session_read["data"] = base64_decode($session_read["data"]);
return $session_read["data"];
}
}
function write ($id,$data) // do not modify function parameters
{
if(!$data) { return false; }
global $session_connection, $session_read, $session_expire, $table;
$expire = time() + $session_expire;
$data = mysql_real_escape_string(base64_encode($data));
if($session_read) $query = "UPDATE `$table` SET data=\"{$data}\", expire=\"{$expire}\" WHERE id=\"{$id}\"";
else $query = "INSERT INTO sess_data SET id=\"{$id}\", data=\"{$data}\", expire=\"{$expire}\"";
mysql_query($query,$session_connection);
return true;
}
function close ()
{
global $session_connection;
mysql_close($session_connection);
return true;
}
function destroy ($id) // do not modify function parameters
{
global $session_connection,$table;
$query = "DELETE FROM `$table` WHERE id=\"{$id}\"";
mysql_query($query,$session_connection);
return true;
}
function gc ($expire)
{
global $session_connection,$table;
$query = "DELETE FROM `$table` WHERE expire < ".time();
mysql_query($query,$session_connection);
}
// Set custom handlers
session_set_save_handler ("open", "close", "read", "write", "destroy", "gc");
// Start session
session_start();
?>
MySQL Database Description
create table sess_data (
id2 int not null auto_increment,
id text not null,
data text,
expire int not null,
primary key(id2)
);
You can't read cookies from one domain in another domain. That's a security thing implemented in the browser. Using a database for sessions allows you to have multiple servers share sessions on the same domain, but does not allow for multiple domains on the same server to share sessions.
If you want to share sessions between domains, you would need to implement some sort of session transfer method when you switch domains. The simplest way to do this would involve passing the session id as a GET parameter from a page on one domain to a page on the other. Then, on the other domain, you would pick up the session id and create a new session using that ID.
While that is a simple way to do it, it isn't very secure and allows for session hijacking. A better way would be to use the database to create a record with the session id in it, set a short timeout on it, and pass the ID of that record to the other domain. The other domain would then pick up the record from the database and create a session with it. If the record in the database is past it's expiration, it wouldn't pick up the session. This would provide better protection against session hijacking.
This is the purpose of session_name(). Assign a different name to each application's session to avoid collisions between $_SESSION keys. The name will be used as the session cookie's name so although both session cookies will be passed to both applications, only the one matching the application's session_name() will be used to populate $_SESSION.
// App 1
session_name('app1');
session_start();
// App 2
session_name('app2');
session_start();
You really should look into SSO (single sign-on). One option for SSO is to use OpenID (as used on SO), and using it will make your life a lot easier.
Here's an article on it : http://devzone.zend.com/article/3581
the cookies and their visibility is a problem. The browser accessing the new site would not send the session id of the old site to the server.
I think your read() does not use the ssid parameter you provide as session id but as the browser has no session with this domain the system generates one with new id as $id. Have a look if $_REQUEST['ssid'] exist in the database.
Custom session handler might a bit big for this job. You could just check if $_REQUEST['ssid'] exist in the session database and rewrite $_SESSION with it.
I was wondering if anyone could give some suggestions on my method for sharing sessions between domains on same server (same cookie storage folder).
In each pages HEAD tag on all my sites, I call the following PHP code
if(!isset($_SESSION['sso'])) {
require_once('database.php');
$sites = array('http://site1', 'http://site2');
session_regenerate_id(); //Make new session id that will be shared
$session_id = session_id();
foreach($sites as $site) {
if($site != CURRENT_SITE) {
$sesh_key = md5(SALT.$site.$session_id);
$database->insertSessionId($sesh_key, $session_id);
$url = sprintf('%s/sso_set.php?k=%s', $site, $sesh_key);
echo('<link type="text/css" rel="stylesheet" href="'.$url.'" />');
}
}
$_SESSION['sso'] = 'SET';
}
Then on each site I have a file called 'sso_set.php' which contains
<?php
session_start();
if(!isset($_SESSION['sso'])) {
require_once('database.php');
$key = $_GET['k'];
$session_id = $database->getSessionId($key);
if($session_id) {
session_destroy();
session_id($session_id);
session_start();
$database->deleteSessionId($key);
$_SESSION['sso'] = 'SET';
}
}
Is using a text/css link a good idea?
I figured this is always called even if Javascript or Images are disabled?
This code basically makes the first site out of all my sites that gets opened by the user sets the Session ID, and then passes it on to the other sites.
Seems to work pretty well.
You get a slight delay the very first time any of the sites opened and the ID is passed to the sites. But, you could do this via AJAX so the page loads fast. But, then you rely on Javascript being enabled.
Thoughts?

PHP cookies in a session handler

I have run into a very interesting problem trying to debug my custom PHP session handler. For some reason unknown to me I can set cookies all the way through the session handler right up until the very start of the write function.
As far as I know session handler calls go in this order.
open -> read -> write -> close
The open function sets a cookie just fine.
function open($save_path,$session_name)
{
require_once('database.php');
require_once('websiteinfo.php');
mysql_connect($sqllocation,$sql_session_user,$sql_session_pass);
#mysql_select_db($sql_default_db);
date_default_timezone_set('America/Los_Angeles');
setcookie("test","test");
return TRUE;
}
The read function can set a cookie right up until the very moment it returns a value.
function read($session_id)
{
$time = time();
$query = "SELECT * FROM 'sessions' WHERE 'expires' > '$time'";
$query_result = mysql_query($query);
$data = '';
/* fetch the array and start sifting through it */
while($session_array = mysql_fetch_array($query_result))
{
/* strip the slashes from the session array */
$session_array = $this->strip($session_array);
/* authenticate the user and if so return the session data */
if($this->auth_check($session_array,$session_id))
{
$data = $session_array['data'];
}
}
setcookie("testcookie1","value1",time()+1000,'/');
return $data;
}
The very first line of the write function is setting another cookie and it cannot because the headers are already sent.
From the manual for session_set_save_handler():
Note: The "write" handler is not
executed until after the output stream
is closed. Thus, output from debugging
statements in the "write" handler will
never be seen in the browser. If
debugging output is necessary, it is
suggested that the debug output be
written to a file instead.
Essentially, writing changes to session data (calling the registered session write function) does not happen until PHP is almost completely done with its execution cycle. By this time, all your output has already been sent and it is not possible to modify the header information, so setcookie() won't work.
You can have it write data earlier by using session_write_close().

Categories