Session issue with unset() and the browser back button - php

I'm using a SESSION variable to set the pre-allocated record id from Postgres (getting the record id sequence) using PEAR's DB nextId(). I set the I id to a session variable because of scoping issues in the legacy code I'm working with. After the INSERT happens I use the unset() to remove the SESSION variable (This is to clear the used record id). There is more information in the session as well that remains intact, it's just the next_id element that is being removed.
Pseudo code:
// Get the next Id and set to SESSION
$_SESSION['next_id'] = $db->nextId();
... more code here
// After insert
unset($_SESSION['next_id']);
My question is, Would clicking the back button on the browser somehow reset the SESSION variable $_SESSION['next_id']? Maybe causing it to be NULL? How does CACHE handle the SESSION after an element has been removed but the user has returned to a previous state?
EDIT:
the reason for the question is that the code in production is randomly (by any user) trying to INSERT with a NULL record id (Which is the next_id from SESSION). Just trying to debug the process as the code is very minimal, reviewed by my peers and has just stumped us???
EDIT 2:
Wow I guess there is an issue with how I'm using the SESSION variable.
Well I'll explain as much as I can as to why I chose this method.
First the code is in the process on being rewritten from PHP 4, but the current production version is mostly PHP 4 with a ton a newly added module code in PHP 5.
The reason I chose the SESSION variable is because of a scoping issue that would need to be hard coded on several hundred pages of legacy code or on one page and cast the value into the SESSION which all pages have access to. (well my boss who pays my salary like the option I chose).
The original problem is they (my boss) wanted to display the id to the end user before the insertion of the information. Hence the PEAR DB call nextId(). We use PostgreSQL and I'm using the record id sequence to ensure that the next id will be unique and allocated to the end user only (No duplicates as Postgres handles locking this in the sequence).
So the end user can also navigate to multiple pages during the process, this also is another scoping issue.
Now using the SESSION and retrieving the next Id with some validation and checks is about 50 lines of code for the whole process instead of the thousands of lines of code that would have been written to do this the correct way.
Again NOT MY CODE in the first place, just making the best solution at the least possible cost.
If you have another, better, greater, easier, cheaper solution feel free to post away. But if you're going to piss on my decision about how to code, then please pay my bills and I will fully agree with following better coding standard,practices,solution.

Can't imagine a scenario where you would need to store nextId() in session to be used on a next page.
As webbiedave pointed out - $_SESSION is stored on a server, so no - hitting back button will not "reset" the session variable.
But, if user hits refresh on a second page (the one that cleared the _SESSION variable) your script will be launched again, with next_id set to null (because it is set to nextId() on a previous page)
The same will happen if a user hits the back button and the previous page will be loaded from browser cache - no request to a server, no next_id variable in a _SESSION.
But still, there is something really wrong if you store nextId() in a _SESSION.

Just bypass the insert all together if $_SESSION['next_id'] comes up NULL, even if it means calling die() before you reach that legacy code you don't want to touch.

Are you sure the session itself isn't being flushed on the server-side? I had a situation in a shared-hosting environment where the server settings were caused my sessions to disappear. I had to add local settings in my app to overcome this (private rather than common session storage).
These are the settings I have in my .htaccess file for the sessions, giving them reasonable time on the disk
php_value session.gc_probability 1
php_value session.gc_divisor 100
php_value session.gc_maxlifetime 3600

Found the cause of the issue. End users are opening multiple tabs in their browsers causing the SESSION data to span to any open tab as they all would call the same session. So submitting one request in one tab, unsets the session in all tabs. So when they submit the request in the second tab the session is missing the next_id value causing all the problems. User training will solve the issue for now but looking to implement things in a new way soon.
Thanks for all the efforts

Related

Handling PHP Sessions With Multiple Front End's

We have 2x pfSense FW's in HA, behind that, 2x Zen Load Balancers in Master/Slave Cluster, behind those, 3x Front End web stack servers running NGinx, PHP-FPM, PHP-APC. In that same network segment, there are 2x MySQL DB Servers in Master/Slave replication.
PHP sessions on the front ends should be "cleaned up" after 1440 seconds:
session.gc_maxlifetime = 1440
.
Cookies are expired when the users browser closes:
session.cookie_lifetime = 0
Today, we were alerted by an end user that they logged in (PHP based login form on the website), but were authenticated as a completely different user. This is inconvenient to say the least.
The ZLB's are set to use Hash: Sticky Client. They should stick users to a single Front End (FE) for the duration of their session. The only reason I can think of this happening is that two of the FE's generated the same PHP Session ID, and then somehow the user was unlucky enough to be directed to that other FE by the LB's.
My questions are plentiful, but for now, I only have a few:
Could I perhaps set a different SESSID name per front end server? Would this stop the FE's generating session ID's that were the same? This would at least then result in the user getting logged out rather than logged in again as a different user!
We sync the site data using lsyncd and a whole bunch of inotifywatch processes, but we do not sync the /var/lib/php directories that contain the sessions. I deliberately didn't do this... I'm now thinking perhaps I should be syncing that. lsyncd will be able to duplicate session files across all 3 front ends within about 10seconds of the sessions being modified. Good idea as a temporary fix?
Lastly, I know full well that the client should be using the DB to store sessions. This would completely eradicate it being able to duplicate the session ID's. But right now, they are unwilling to prioritise that in the development time-line.
Ideas very much welcome as I'm struggling to see an easy way out, even as a temporary measure. I cant let another client get logged in as a different user. It's a massive no-no.
Thanks!!
Judging by your question you are somewhat confused by the problem - and its not clear exactly what problem you are trying to fix.
Today, we were alerted by an end user that they logged in (PHP based login form on the website), but were authenticated as a completely different user
There's potentially several things happening here.
Cookies are expired when the users browser closes:
Not so. Depending on how the browser is configured, most will retain session cookies across restarts. Since this is controlled at the client, its not something you can do much about.
PHP sessions on the front ends should be "cleaned up" after 1440 seconds
The magic word here is "after" - garbage collection is triggered on a random basis. Session files can persist for much longer and the default handler will happily retrieve and unserialize session data after the TTL has expired.
Do you control the application code? (if not, your post is off-topic here). If so, then its possible you have session fixation and hijack vulnerabilities in your code (but that's based on the description provided by the user - which is typically imprecise and misleading).
Its also possible that content is being cached somewhere in the stack inappropriately.
You didn't say if the site is running on HTTP, HTTPS or mixed, and if HTTPS is involved, where the SSL is terminated. These are key to understanding where the issue may have arisen.
Your next steps are to ensure that:
you have logout functionality in your code which destroys the session data and changes the session id
that you change the session id on authentication
That your session based scripts are returning appropriate caching information (including a Varies: Cookie header)
It is highly improbable that 2 systems would generate the same session id around the same time.
Really you want to get away from using sticky sessions. It's not hard.
You've got 2 layers at your front end that are adding no functional or performance value, and since you are using sticky sessions, effectively no capacity or resillience value!!! Whoever sold you this is laughing all the way to the bank.

How to keep extending session life when user is active?

Let's say there's a site/system with a logged in member area, and users are rarely, but very inconveniently logged out while working with the site/system.
It's doubtfully session expiring, since the user was not idle for very long. And even if they were idle, I added a periodic AJAX request, a so called heartbeat, which updates the sessions' access time, and modified time. I even added a touch($session_file) every time a user clicks something or a heartbeat is called. I tried regenerating session ID as well. Nothing helped.
And unfortunately, so far, I was not able to reproduce the problem locally, because it happens every so often, when there's more requests. Some php.ini parameters:
session.use_cookies = 1
session.use_only_cookies = 1
session.cookie_lifetime = 0
session.gc_probability = 1
session.gc_divisor = 1500
session.gc_maxlifetime = 10800
Since sessions and authentication is already handled via one super controller in your code, it should be easy to at least rule out session destruction.
Typically only the login page creates a session, so at this point you can (and should) add a known value inside, such as the session id.
The other pages (including your heartbeat) resume an existing session, so at this point you look for the above value; if it's missing, you can do a few more checks:
was a session cookie passed? if not, browser / cookie issue.
does the session cookie correspond with session_id()? if not, session file was lost due to garbage collection.
does the known value exist in the session? if not, session was truncated or someone is trying to do session adoption attack.
does the known value correspond to the session cookie? if not, the session was established via different means than cookie; you could check session.use_only_cookies setting.
The above set of checks should point you in the right direction.
I presume you are using built in PHP file session storage? There are known race conditions problems with it.
I had similar issues with loosing session id's when there were concurrent requests from same session id. Since file was locked by first request all other concurrent connections were unable to access file and some of them generated new session id. Those situations were also very rare and it took me time to locate the problem. Since then I'm using memcached for session storage and those problems vanished.
It is hard for me to answer this without further information however are you sure this only happens when you have more traffic. What can be done:
Add the codez
OS distro and version
PHP version
Do you have any session checks that rely on the IP. If so it might be that you are actually using a proxy which gives a different IP once every so oftern (like Cloudflare) in which case you can use the HTTP_CF_CONNECTING_IP to get the actually constant IP.
I have also noticed one more thing:
session.use_only_cookies = 1
session.cookie_lifetime = 0
So the cookie will actually be destroyed when the browser closes. Is it possible the connection is lost because the user closes the tab (which in some browsers does actually clear the cookies)?
As others have suggested I would strongely say you move to a DB session. Using a central session store can help in stopping race conditions, it won't stop them completely but it does make it harder. Also if you really get that many session ids you might wanna look into using something other than the built in PHP session id, however the built in hash is extremely unique and has a very low chance of being duplicate so I don't think that's your problem.
Clarification Edit:
If you are using a different proxy to Cloudflare then you will, of course, need to get the HTTP header thart your proxy sets, using Cloudflare is an example.
Here's something I've used. It uses javascript to 'ping' the server on an interval of 10 minutes.
<form method="POST" target="keepalive-target" id="keepalive-form">
<input type="hidden" name="keepalive-count" id="keepalive-count">
</form>
<iframe style="display:none; width:0px; height:0px;" name="keepalive-target"></iframe>
<script>
var kaCount=0;
setInterval(function()
{
document.getElementById("keepalive-count").value=kaCount++
document.getElementById("keepalive-form").submit();
},600000);
</script>
All the provided answers have shown good insight to the question, but I just have to share the solution to my exact problem. I was finally able to reproduce the problem and fix it.
So the system contained two subsystems, let's say admin and client interfaces. The admin was unexpectedly logged out when they logged in as client in another tab and logged out the client interface while being logged in as admin. It was doing this because everything was written to one session with namespaces. What I did is remove the code that kept destroying the session on logout action, and replaced it with session namespace unsetting and replacing with guest session for that namespace that only has access to the login page.

PHP Session not written after Output(echo or print_r on external ajax Call)

I´m having some serious trouble debugging this particular problem and I hope someone has a clue what i´m doing wrong.
I have a Custom CMS system working that uses Paragraphs as building blocks that get updated using Ajax(prototypejs) calls and functions that parse the HTML chunks in given order, clean them up and save this data in associative arrays in a Session variable.
Users log in, Session is created and I can check for this session without problem in every page I need it. The system works directly on the definitive websites, so the user can see his updates on realtime and browse the site as a normal user would do, but editing.
So, nothing new here. But here is the weird thing.
Enduser site on edit mode(admin user logged in): path "/"
After the logged status is verified, a function processes the editable content and saves an associative array to session, it also starts some javascript objects for editing every paragraph. Data is actually saved, I can use an external script to check if it´s there after this php script ends.If I load a new page(new content), Session gets updated with new data)
Admin User modifies a paragraph using an Inplaceeditor and this HTML chunk is send via Ajax to a php script that starts the named session, reads the present session data, checks if a paragraph should be modified, appended or deleted and reassigns values to existing array keys in $_SESSION. if i make a var_dump() o print_r to $_SESSION after assigning new data is there.After that the script echoes the processed html, and ajax updates the original paragraph on the calling page.
This script is in /admin/cms/...etc, that means at least 4 directories inside the root of the site.
When the script ends, I check using the same session dump script to see if data was really written/commited, but no, $_SESSION has only the original data from the calling page.
Same ID, same session name, same session_start() but no data gets written.
This whole operation is very quick, so I though it could be a speed problem, scripts ends before session_write_close can make his work.
But if I add a new key to $_SESSION array and put some data there, data gets updated and written. If i don´t output anything on this script and just process data and set session variables it also get´s updated and written.
It´s like some members of $_SESSION array are getting blocked to update.
What i did to track this error and what i´m sure i´m not doing wrong.
1.- register_globals are off of course
2.- session_name() and session_start() are always present and in the given
order. I used to have multiple
session_start() -close on a same page
to use several named sessions, but to
refine the problem this is not longer
so.
3.- I use session_write_close() after session data is processed. Also
tried without, letting php decide
when to commit data, but no luck.
4.- I`m using only cookies for SID.
5.- sessions are stored on /tmp, i can see the data getting updated.
I also tried using a custom save
handler on DB, but same problem,
"_write" got only called when no output as present.
I searched php.net, stackoverflow, google, etc for this subject. I never ask without investigation, this is my first time in many years...but it´s just so unlogical it must be something tiny a haven´t thought of.
The most weird thing is that when I just process data without output $_SESSION gets updated ok. But if i modify this script afterwards by adding the output and try again, instead of just having the new(last) value present I get the original value back, the one created by the calling page at first place, sometimes after playing around a few times! PHP can´t cache values between scripts or?I dont have globlals on.
I´m really clueless. This system worked flawless on PHP4.3, since i´m using 5.3.3 for two moths my users where caliming data where getting mixed up, so i checked and yes, there are serious problems. Today I updated to (5.3.6) and I can´t get this session values commited.
Script code called via Ajax:
<?
session_cache_limiter('nocache');
session_name("CMS_ses");
session_start();
include('../htmLawed/htmLawed.php');
include("utils_cms.php");
include("../../../php/utils_array.php");
$value=$_POST['value'];
$editorId=$_POST['editorId'];
$clase=$_POST['clase'];
$editorId=str_replace("pre","",$editorId);
$value=html_entity_decode(stripslashes($value),ENT_QUOTES);
if (strlen(trim($value))==0)
{
die();
}
$value="<div id=\"$editorId\" class=\"$clase\">$value</div>";
$newXHTML=$value;
$retorno=CMS_nuevoBloque($newXHTML,$editorId);
$_SESSION['data']['CMSeditores']=$retorno[1];
$_SESSION['data']['CMScont']=$retorno[2];
session_write_close();
print_r($retorno[0]); //Offending part...without everything works
?>
really nothing strange here....main page code is even simpler, no strange php directives, etc.
Here is the header of the caller page
include 'php/db.php';
$len=$_GET['len'];
$sec=$_GET['sec'];
$cont=$_GET['cont'];
$admfin=$_GET['admfin'];
$fecha=$_GET['fecha'];
$token=$_GET['token'];
$cur=$_GET['cur'];
$PHP_SELF=$_SERVER['PHP_SELF'];
session_cache_limiter('nocache');
session_name("CMS_ses");
session_start();
$passvar='';
unset($adm);
if ((!empty($_SESSION['cms_logged'])) and (!isset($admfin)) )
{
$nivelpermisos=$_SESSION['cms_logged_group'];
$useractual=$_SESSION['cms_logged'];
$adm=1;
}
elseif (empty($_SESSION['cms_logged']))
{
unset($useractual);
}
//.........rest of the code
UPDATE: I did late night tests and found someting i don´t understand.HElP please:
It has not only to do with Sessions but also with Mysql Querys. Same code, but instead of trying to write to $_SESSION array i made a simple update to a Innodb table using the session_id. When i Output some code, the update does get executed,(i can output the query string and no mysql_error() or notice) problems, but checking the database the row doesn´t get updated. Letting the output out if the script and Query does get commited. Only common thing is sessions are started and output is made.
I restarted Apache, etc(who knows) but no luck. Then i made something really stupid, because this is a server side thing. I changed my browser to Firefox(using safari) and everything works! Ok, recheck, back to safari, nothing works. Both running side by side, same issue. PHP is server side, how can different browsers handle code different, can a browser say to apache rollback, request not handled or call the same script twice without notice(checked safaris developer console and the script is called only once) ? Can safari resubmit data silently because it "thinks" ajax failed? I checked headers using firebug and Safaris developer tools , nothing strange but whenever i make a Ajax call with safari, the caller page reloads data(Aka conection to server...).
I really don´t understand nothing.
I had a similar problem to this (if I have understood correctly). I needed to force session data to be written (for a custom session driver) after scripts have finished running. A shutdown function can be registered which should run after scripts have finished.
Maybe this will solve (or help you to solve) your problem.
http://php.net/manual/en/function.register-shutdown-function.php
Thank's for your help. I was doing everything in the right order and still session data was not being written. Session names where necesary because sometimes we test many sites on the same domain using the same custom CMS. So, finally, after making lots of test and no luck, i found that register globals was active on this server(we never use it, code was written having this option off in mind of course), but it messes with sessions!. Switching this off made a huge change. No more problemas. I also made a custom session handler in DB, so i could track the problems in an more centralized way.
Conclussion: Never use register globals + named sessions, an complex data in sessions.
Anyway, i will give this issue more time and more tests. Ajax calls are also sometimes too fast, i had to put a sleep command so writing the session data was really done.Thanks
I am not sure but few suggestion i think may be helpful.
delete session cookies before refreshing the page for testing purposes :)
Ensure that you're not assigning any arrays with a key containing the pipe character (|). This will prevent the session data from being serialized and saved.
Do session_regenerate_id(true); many cases session_write_close doesn't seem to matter with out session_regenerate_id. or just do session_start() after session_write_close() if you are relying on SID ; and in your case i think this is what is causing problem to you as you are ending the current session every time and not re starting it for the next page. hope u get my point. Further more To Make sure data is actually flushed out to the browser use ob_end_flush();
i could not understand the connection between
$_SESSION['data']['CMSeditores']=$retorno[1];
$_SESSION['data']['CMScont']=$retorno[2];
and
$nivelpermisos=$_SESSION['cms_logged_group'];
$useractual=$_SESSION['cms_logged'];
i think you need to paste some more code where the data part is causing problem instead of admin login part.
i hope this helps you.:)
Is there any reason you're establishing the session name twice? I've had issues in the past where I would establish the session without a name, then another piece of script (not mine) was naming the session. Even at the end of the script I was able to print out the session variable, but once I went to a new page my session had been forgotten. It wasn't until I copied the name included in the 2nd script into my session call that it was solved.
Check that there's no other session names being used; also, maybe try only naming the session once, at the first call to the session?
Question: Are you calling session_start() first thing... before ANY output to the browser and before any variables are assigned?
Sounds silly but give it a try.
Also, why are you using session names? Really not necessary unless you have a lot of session variables with the same name serving different purposes and if thats the case then you need to fix that first!
I had a similar problem but it was having with ie few years back. IE manipulates the header on its own way and that causes strange php bugs that you can find in php.net archives.
#Diego Pino Navarro, please see this help page and find Safari and it's issues with php.
I also found "Safari "forget" http-authentication's logon-information".

Zend Framework Sessions Being Lost

I'm using sessions to store items in a shopping cart. I can create and persist sessions, but with some strange problems:
When I close the tab in Firefox (not the entire browser), the session appears to have been lost. Sometimes it doesn't happen though but usually it does.
Every single time I refresh the page or go to another page, the session ID changes to a new one. I've confirmed this by looking in the cookie with my browser, and also on the server. Also, there are a max of 4 sessions stored on the server at one time. Is all this normal behavior?
The sessions seem to be lost at random intervals...it could be a few minutes or more than an hour.
I just followed the Zend manual but no luck in solving any of this. In the bootstrap I also have Session::start() and Session::rememberMe(). I'm using normal file storage for sessions, just storing in /var/lib/php5 which I think is where Zend framework likes to put it.
Thanks for any direction
If the session data is persisting but the ID is changing then there is a chance there is a call to session_regenerate_id() in there somewhere.
I have run into this before, and you will want to do something like this where you start your session at, for me this is in my Bootstrap.php
if (!empty($_REQUEST['PHPSESSID'])) {
Zend_Session::setId($_REQUEST['PHPSESSID']);
}
Zend_Session::start();
This should solve the issue. When a user has a session, it typically gets passed with every request.
Check your garbage cleanup time for PHP - session.gc_maxlifetime. If it's short, it deletes your session files from under your nose and makes it appear "random".
The default value is 24 minutes (1440 seconds)
This should be set to (or greater than) whatever your cookie lifetime (session.cookie_lifetime) is set for in your application.

How to re-initialize a session in PHP?

I am attempting to integrate an existing payment platform into my webshop. After making a succesful transaction, the payment platform sends a request to an URL in my application with the transaction ID included in the query parameters.
However, I need to do some post-processing like sending an order confirmation, etc. In order to do this, I'd need access to the user's session, since a lot of order-related information is stored there. To do this, I include the session_id in the intial request XML and do the following after the transaction is complete:
$sessionId = 'foo'; // the sessionId is succesfully retrieved from the XML response
session_id($sessionId);
session_start();
The above code works fine, but $_SESSION is still empty. Am I overlooking something or this simply not possible?
EDIT:
Thanks for all the answers. The problem has not been solved yet. As said, the strange thing is that I can succesfully start a new session using the session_id that belongs to the user that placed the order. Any other ideas?
Not really what you ask for, but don't you need to persist the order into database before you send the customer to the payment-service? It's better to rely on persisted data in your post-processing of the order when you receive the confirmation of the payment.
Relying on sessions is not reliable since you will have no idea on how long this confirmation will take (usually it's instant, but in rare cases this will have a delay).
Also, in the event of your webserver restarting during this time span, will make you lose relevant data.
A third issue is if you have a load-balancing solution, with individual session-managment (very common) then you will have no guarantee that the payment-server and your client will reach the same webserver (since stickiness is usually source-ip based).
I will venture to guess that since domains are different from where the session is set to where you are trying to read it, php is playing it safe and not retrieving session data set by a different domain. It does so in an effort to preserve security in case somebody were to guess session ID and hijack the data.
Workaround for this, assuming the exchange happens on the same physical disk, is to temporary write order data to a serialized (and possibly encrypted depending on wether or not full credit card number is being tracked, which is a whole another story) file that once read by the receiving end is promptly removed.
In essence all that does is duplicates the functionality that you are trying to get out of sessions without annoying security side-effects.
Many thanks for all the replies.
Smazurov's answer got me thinking and made me overlook my PHP configuration once more.
PHP's default behaviour is not to encrypt the session-related data, which should make it possible to read out the session data after restarting an old session from another client. However, I use Suhosin to patch and prevent some security issues. Suhosin's default behaviour is to encrypt session data based on the User Agent, making it a lot harder to read out other people's sessions.
This was also the cause of my problems; disabling this behaviour has solved the issue.
Make sure you're closing the current session before you attempt to start the new one. So you should be doing:
$id = 'abc123';
session_write_close();
session_id($id);
session_start();
Dirty, but has worked for me:
Tell the payment gateway to use
http://yourdomain.com/callbackurl.php?PHPSESSID=SESSIONIDHERE
PHP uses that method of passing a session around itself if you set certain config vars (session.use_trans_sid), and it seems to work even if PHP has been told not to do that. Its certainly always worked for me.
Edit:
Your problem may be that you have session.auto_start set to true - so the session is starting automatically using whatever ID it generates, before your code runs.
How about do it in another PHP page, and you do a iframe include / redirect user to the second page?
I'm not sure the exact length of time between your transaction and your check; but it certainly seems that your session cookie has expired. Sessions expire usually after 45 minutes or so by default. This is to free up more uniqid's for php to use and prevent potential session hijacking.
I'm not sure if you have a custom session handler and whether it's stored in the database but guessing from your posts and comments on this page I would assume it is stored in server side cookies.
Now the solution to your problem, would be to bite the bullet and store the necessary data in the database and access it via the session id, even if it means creating another table to sit along side your orders table.
If however you are doing the action immediately then the other explanation is that either the user logged out or committed an action which destroyed their session (removing the server side cookie).
You will see these cookies in your servers /tmp folder, try have a look for your cookie, it should be named 'sess' + $session_id.

Categories