Is there a way to close a PHP session without either writing or destroying it? Am I missing something or are there just two functions (session_write_close() and session_destroy()) which reset session_status() to PHP_SESSION_NONE? In other words, if I have an open session, can I simply close it without affecting the external session data, so it can be reloaded again.
You can do this by $_SESSION = null. The session is unavailable but the data remains (does not get deleted).
Consider you have data in the session:
<?php
session_start();
session_regenerate_id(); // get a fresh id
echo session_id();
$_SESSION['test'] = '12345';
print_r($_SESSION); // echoes the data in the session
Data: test|s:5:"12345";
Then in the next request:
<?php
session_start();
echo session_id() . "<br>"; // echoes the same id as of the last request
$_SESSION = null;
print_r($_SESSION); // echoes nothing
echo "<br>";
echo session_status(); // echoes 2 (PHP_SESSION_ACTIVE)
Data still the same: test|s:5:"12345";
(php-fpm 5.4.29, most recent nginx, memcached as session handler).
Well the data still can be written through $_SESSION['whatever'] = ... but not read. I'm not sure whether this is a good solution but I have to admit I still don't understand why or whatfor you need this.
As an alternative, you could implement a wrapper class for the session with a property $this->active = true; // or false:
class MySessionWrapper {
protected $active;
protected $data;
// getter setter for $this->active and $this->data ...
public function getData($var) {
if ($this->active !== true) {
throw new Exception('session disabled');
}
}
}
Some months later after raising an issue the two new methods are now available in PHP 5.6, but the documentation arrived only with 5.6.2.
session_abort() – Discard session array changes and finish session, see the docs.
session_reset() – Re-initialize session array with original values, see the docs.
Related
I've got this session variable, which was initiated in index.php
<?php
$sid = session_id();
if (empty($sid) {
session_start();
}
//value of the variable can be changed and saved before coming back to the index
if(!isset($_SESSION['context'])){
$_SESSION['context'] = 1;
}
//...
?>
Later a script is used to create or update a database entry.
entryupdate.php:
<?php
include_once("template.php");
/*....*/
$sid = session_id();
if (empty($sid)) {
session_start();
}
/*....*/
//Create a new entry, context is not given in the $input_object at this point.
$template = new Template($input_object);
$context = $template->getContext();
?>
template.php is a script where only the Template Object is defined, so I don't call $session_start in it, my rationale being that it would always be called by the scripts including it, as it is in the case I'm describing here. Here is the relevant code:
<?php
class Template
{
private $m_context;
//other private parameters
function __construct($arguments_object){
if(!is_null($arguments_object)){
/*....*/
$this->m_context = $arguments_object->context;
/*....*/
}
}
/*....*/
function getContext(){
if(!empty($this->m_context)){
return $this->m_context;
}else{
//this would be our case, as the parameter was not initialized yet.
return $_SESSION['context'];
}
}
/*....*/
}
?>
Now, a colleague stumbled on an error and upon investigation, I found out that the $context was set to NULL. When I tried to reproduce the issue, the context was correctly initialized. And to be honest, for the few years the tool has been used, it is the first time this kind of issue was encountered.
I've also checked, at no point I am unsetting this particular session variable.
Am I making a false assumption there, in thinking that template.php would find the session variable when the session was started in entryupdate.php where it was included and used?
I want to change the session id once the user has logged in to a custom random session id with my own prefix.
However, when I do the below the session data is lost;
Zend_Session:setId('my-new-id');
Zend_Session:start();
But if I do the following the session data will still be available;
Zend_Session:regenerateId();
Zend_Session:start();
Anyidea how can I fix this issue?
see in library/Zend/Session.php:316:
if ( !self::$_sessionStarted ) {
self::$_regenerateIdState = -1;
} else {
if (!self::$_unitTestEnabled) {
session_regenerate_id(true);
}
self::$_regenerateIdState = 1;
}
Best practice is to call this after session is started. If called prior to session starting, session id will be regenerated at start time.
also see: Global Session Management with ZF1:
$defaultNamespace = new Zend_Session_Namespace();
if (!isset($defaultNamespace->initialized)) {
Zend_Session::regenerateId();
$defaultNamespace->initialized = true;
}
added:
Then try this:
$commit = Zend_Session::namespaceGet('Default');
Zend_Session::writeClose(false);
// ^ session_commit(); // see Advanced Usage: http://framework.zend.com/manual/1.12/en/zend.session.advanced_usage.html#zend.session.advanced_usage.starting_a_session
.. change id with session_id("my-new-id");
// start new session ...
$namespace = new Zend_Session_Namespace('Default');
foreach($commit as $idx => $data){
$namespace->$idx = $data;
}
However! after calling Zend_Session::writeClose() we cannot restart session with Zend_Session::start() version of 22744dd
see this issue:
https://github.com/zendframework/zf1/issues/159
fixed version: https://github.com/itscaro/zf1/blob/bbebe445d8551582cba6f5e9c9c1a8396fb2b322/library/Zend/Session.php#L436
anyway, I recommend watching the documentation of the start session - Starting a Session
and/or use Zend_Session::regenerateId(); instead of Zend_Session::setId()
Does Php always create a session file as soon as session_start() is called, even though there's nothing to keep track of (= no variable written in $_SESSION[])? If so, why?
Default PHP file-based sessions encode the session ID into the session file's filename. Since that's the only place the ID is kept normally, SOMETHING has to be kept to store the id. That means you'll get a file created, even if nothing is EVER written to $_SESSION.
In PHP-like pseudo code, basically this is occuring:
function session_start() {
if (isset($_COOKIE[ini_get('session.name')])) {
// session ID was sent from client, get it
$id = $_COOKIE[ini_get('session.name')];
} else {
// no session ID received, generate one
$id = generate_new_id();
setcookie(ini_get('session.name'), $id, ......);
}
$session_file = ini_get('session.save_path') . '/sess_' . $id;
if (file_exists($session_file)) {
// found a session file, load it
$raw_data = file_get_contents($session_file);
$_SESSION = unserialize($raw_data);
} else {
// brand new session, create file and initialize empty session
file_put_contents($session_file, serialize(array());
$_SESSION = array();
}
// lock the session file to prevent parallel overwrites.
flock($session_file);
}
I've been working on the security of my site (PHP) and there's a ton of information to ingest. I've tried to implement security I've researched on OWASP, but one thing I'm a little nervous about, among other things, is how to handle SESSIONS when the user logs out.
Currently all I'm using is:
session_destroy();
But, I've read that I should change the XRSF token and start another SESSION so it forces the user to resubmit login credentials in-turn explicitly ending the users SESSION.
Is session_destroy() enough?
EDIT
I've downloaded michael-the-messenger, which I believe was created by Michael Brooks (Rook) which should be VERY secure, and I saw some code that I might want to use. Is this something that could safely replace the session_destroy() I'm using?
CODE
if($_SESSION['user']->isAuth())
{
/* if they have clicked log out */
/* this will kill the session */
if($_POST['LogMeOut'] == 'true')
{
//When the user logs out the xsrf token changes.
$tmp_xsrf = $_SESSION['user']->getXsrfToken();
$_SESSION['user']->logout();
$loginMessage = str_replace($tmp_xsrf, $_SESSION['user']->getXsrfToken(), $loginMessage);
print layout('Authorization Required', $loginMessage);
}
else
{
header("Location: inbox.php");
//user is allowed access.
}
}
else
{
// code goes on ....
LOGOUT
public function logout()
{
$_SESSION['user'] = new auth();
}
Obviously $_SESSION['user'] = new auth(); reinstantiates the object which sets a private variable $auth to false.
but one thing I'm a little nervous about, among other things, is how
to handle SESSIONS when the user logs out.
According to manual:
In order to kill the session altogether, like to log the user out, the
session id must also be unset. If a cookie is used to propagate the
session id (default behavior), then the session cookie must be
deleted. setcookie() may be used for that.
So, in order to safely destroy a session, we'd also erase it on the client-machine.
session_destroy() along with setcookie(session_name(), null, time() - 86400) will do that.
Apart from that,
What you are doing wrong and why:
Session storage merely uses data serialization internally. By storing
an object in the $_SESSION superglobal you just do
serialize/unserialize that object on demand without even knowing it.
1) By storing an object in $_SESSION you do introduce global state. $_SESSION is a superglobal array, thus can be accessed from anywhere.
2) Even by storing an object that keeps an information about logged user, you do waste system memory. The length of object representation is always greater than a length of the strings.
But why on earth should you even care about wrapping session functionality? Well,
It makes a code easy to read, maintain and test
It adheres Single-Responsibility Principle
It avoids global state (if properly used), you'll access session not as $_SESSION['foo'], but $session->read['foo']
You can easily change its behaivor (say, if you decide to use DB as session storage) without even affecting another parts of your application.
Code reuse-ability. You can use this class for another applications (or parts of it)
If you wrap all session-related functionality into a signle class, then it will turn into attractive:
$session = new SessionStorage();
$session->write( array('foo' => 'bar') );
if ( $session->isValid() === TRUE ) {
echo $session->read('foo'); // bar
} else {
// Session hijack. Handle here
}
// To totally destroy a session:
$session->destroy();
// if some part of your application requires a session, then just inject an instance of `SessionStorage`
// like this:
$user = new Profile($session);
// Take this implementation as example:
final class SessionStorage
{
public function __construct()
{
// Don't start again if session is started:
if ( session_id() != '' ) {
session_start();
}
// Keep initial values
$_SESSION['HTTP_USER_AGENT'] = $_SERVER['HTTP_USER_AGENT'];
$_SESSION['REMOTE_ADDR'] = $_SERVER['REMOTE_ADDR'];
}
/**
* You can prevent majority of hijacks using this method
*
* #return boolean TRUE if session is valid
*/
public function isValid()
{
return $_SESSION['HTTP_USER_AGENT'] === $_SERVER['HTTP_USER_AGENT'] && $_SESSION['REMOTE_ADDR'] === $_SERVER['REMOTE_ADDR'] ;
}
public function __destruct()
{
session_write_close();
}
/**
* Fixed session_destroy()
*
* #return boolean
*/
public function destroy()
{
// Erase the session name on client side
setcookie(session_name(), null, time() - 86400);
// Erase on the server
return session_destroy();
}
public function write(array $data)
{
foreach($data as $key => $value) {
$_SESSION[$key] = $value;
}
}
public function exists()
{
foreach(func_get_args() as $arg){
if ( ! array_key_exists($arg, $_SESSION) ){
return false;
}
}
return true;
}
public function read($key)
{
if ( $this->exists($key) ){
return $_SESSION[$key];
} else {
throw new RuntimeException('Cannot access non-existing var ' .$key);
}
}
}
Maybe session_unset() is what you are looking for.
I'm working with a third party COM object from PHP and I want to save the object on a session variable to access it in future server calls.
If I save the object in session:
$_SESSION['collItem'] = $collItem;
I can access it's methods and properties through $_SESSION['collItem'] inmediately after definition.
But, in future calls to server, if I try to use $_SESSION['collItem'], I can't access it again.
I write here some more code to clarify.
Method to initailize COM object in my script "functions.php":
public function setAppData() {
try {
$appD = new COM('ASData.CASDataApp');
$appD->InitMasterData(true, 1, 91);
$appD->DateMask = 'ymd';
$_readDB = $appD->InitApp($this->readDB());
} catch (Exception $e) {
$err = 'Connection error: ' . htmlentities(addslashes(strip_tags($e->getMessage())));
$this->setError($err);
return false;
}
$appD->appPath = str_replace('\app\include', '', __DIR__);
$this->iniciarCollections($appD);
$this->appData = $appD;
}
Call to method from my script "edit_json.php":
require_once('functions.php');
if (!session_id()) {
session_start();
}
// We recover $mbw object saved in session and initialize COM object
if (isset($_SESSION['mbw'])) {
$mbw = unserialize($_SESSION['mbw']);
}
$mbw->setAppData();
$appData = $mbw->getAppData();
$_SESSION['appData'] = $appData;
If I try access $_SESSION['appData'] inmediately after COM initialization I can work with it without problems, but if I try next code in future server calls (whith $appData object saved in $_SESSION['appData']:
if (!session_id()) {
session_start();
}
$appData = $_SESSION['appData'];
// I can't work with $appData object if I don't initialize it again
Reinitializing COM object isn't a good solution for me because I lose all changes I made.
Use session_start() at the beginning of your script. You need it to retrieve session data from the server into the $_SESSION variable.
From http://php.net/manual/en/function.session-start.php :
session_start() creates a session or resumes the current one based on a session identifier passed via a GET or POST request, or passed via a cookie.
You should edit your question and include your getAppData() method, as well, so we can do a better diagnostic. But what I can guess is that your getAppData() returns an object, and, in that case, when you have a code like
$appData = $mbw->getAppData();
$_SESSION['appData'] = $appData;
what PHP is doing is saving in $_SESSION['appData'] only a reference to the $mbw->getAppData() object, and not the real object.
If you want to store the real object in session, you would have to do the following:
$_SESSION['appData'] = serialize($appData);
and then, whenever you want to use the stored object, you would do:
if (isset($_SESSION['appData'])) $appData = unserialize($_SESSION['appData']);
on the beggining of every file that uses the $appData that was saved in the session.