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.
Related
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.
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
I have been having this problem for some time now, I dont exactly know that if this is the issue but I am pretty confident that it is, I have my remember me session set too expire after 1 week, but when I go to my site after a few hours of inactivity my remember me session is gone, i check my servers tmp dir and the session flat file is gone, what i think is happening is some PHP session garbage collector runs every now and then, but i dont want it to delete these sessions that are suppoes to be stored for a week, how do a modify this behavior?
You're confusing two things.
A "remember me" mechanism doesn't rely on sessions. It relies on a cookie that stores credentials which are used to start a session. In this case, you have to setup the cookie so that is last for one week. See this answer.
If you just want to extend the lifetime of sessions, you have to both extend the lifetime of the session cookie to one week and delay garbage collection. This is done changing session.gc_maxlifetime.
I am trying to make some changes to an opensource project. I want to keep track of when users log in and log out.
Right now I change their login status in db when they login or manually log out. The problem right now is that I cannot find out if the user just closed their browser without pressing on logout button.
For this reason I need to trigger a function that will change database every time the user's session expires.
I've tried session_set_save_handler in PHP, but it looks like I need to override the whole session behavior. What I am looking for is to keep default session behavior and just add functionality when the user's session expires. Is there a way to do that?
I did something really nasty once. Every time a session was "updated" by a page refresh / fetch / etc., I updated a timestamp on a DB row. A second daemon polled the DB every 10 minutes and performed "clean-up" operations.
You won't find any native PHP facilities to achieve your goal. Session timeout doesn't run in the background. You won't even know if a session is timed out, unless a timed out session attempts another access. At this point, nearly impossible to trap, you can make your determination and handle it appropriately.
I'd recommend a queue & poll architecture for this problem. It's easy and will definitely work. Add memcached if you have concerns about transaction performance.
I presume you're using standard PHP file-based sessions. If that's the case, then PHP will do its own garbage collection of stale sessions based on the session.gc_* configuration parameters in php.ini. You can override those to disable the garbage collector completely, then roll your own GC script.
You could either check the timestamps on the files (quick and easy to do in a loop with stat()) to find 'old' sessions, or parse the data in each file to check for a variable that lists the last-access time. Either way, the session files are merely the output of serialize($_SESSION) and can be trivially re-loaded into another PHP instance.
What about window close event on javascript. So basically session is destroyed when all of the windows of the session site are closed. So, when the last window is closed ( this is checked via additional js checking ) send ajax request to server.
Is it possible to configure PHP sessions to never expire? I currently have the default 24 minutes set in php.ini - I could whack this up to a couple of weeks or something like that but I was wondering if I can set them to infinite lifetime?
I want to achieve a similar effect to Stackoverflow's: I never have to log in here. Is this achieved on SO with a never-expiring session or some other means?
Also, as a secondary question: How do the expired session files get cleaned up? If someone creates a session and never returns, which process is cleaning up their expired file?
Normally, what appears to be an everlasting session is two things: a session, which expires pretty soon, and a very long-life cookie containing an auto-login token.
There's a great series of responses on sessions and logging-in contained in this StackOverflow question: The Definitive Guide To Website Authentication
Regarding your question about when sessions are cleaned up, there are several php.ini settings for controlling when the garbage collection for sessions is triggered.
Since PHP both allows and encourages you to create your own session data storage handlers, there is no single correct answer to this question.
Answer to secondary question
Session file cleanup is controlled by the following 3 php.ini settings:
session.gc_probability (default value 1)
session.gc_divisor (default value 100)
session.gc_maxlifetime (specified the age after which the session is considered as garbage)
First 2 settings specify the probability of the garbage collection process being started at session start (before or at the very beginning of your script execution, depending on how you have set things up)
In default configuration there is a 1% probability then that this happens. If it does, then files that are older than maxlifetime are cleaned.
As for your first question - why not write a custom session handler, that stores sessions inside the database (if you have one). That way you can see and control the sessions right from inside the database. Handy :)