开发者

PHP sessions expiring early

I'm having problems with the PHP site I run at work where users are being logged out after a few minutes (the exact time varies, but it is frequent enough to be an issue), regardless of whether they have been actively using the site or not.

The difficulty is that I can't reproduce this problem, if I login as the same users using the same browser I don't get logged out, which suggests it is not a case of the site being completely broken. Unfortunately I don't have access to the user machines to run any traffic-sniffing software.

The things I have already checked are:

  • Asking users to try different browsers. This doesn't seem to solve the problem and isn't a long-term solution anyway as I can't dictate which browsers customers will use.
  • The server time is correct and in line with the user machines.
  • The user Apache runs as has permission to write to the session folder, and I can see the session files being created and their modification times being updated.
  • No output buffering functions are being used.
  • The problem is happening on a variety of pages which seem to have nothing in common (i.e. it's not that they all use AJAX, or update the database or some other reason).
  • Users only access their account from one machine, i.e. they don't do a bit of work on their laptop, switch to the desktop and then wonder why they've been logged out on their laptop (we don't allow multiple simultaneous logins for the same user).

The session settings in PHP are the Debian defaults, and haven't been changed in a .htaccess file or anywhere else. The main ones are:

session.cookie_lifetime    0
session.gc_divisor    100
session.gc_maxlifetime    1440
session.gc_probability    0
session.save_handler    files
session.save_path    /var/lib/php5
session.use_cookies    On

Debian deletes sessions through a cron job instead of using PHP's garbage collector, which is why gc_probability is set to 0. The PHP version we're running is: PHP 5.2.6-1+lenny13 with Suhosin-Patch 0.9.6.2 (cli) (latest version in Lenny, we'll be upgrading to Squeeze soon but I don't think that is the cause of the problem).

We use Zend_Session to manage sessions, and an instance of Zend_Session_Namespace is created once on every page, thus automatically calling session_start(). Sessions are cleared by calling Zend_Session::destroy() on the logout page, so the only ways a user should be logged out are:

  • If they explicitly click the logout link (we log when this happens and it doesn't seem to be the case that browsing are pre-fetching the page and thus logging the user out).
  • If they leave the session inactive for more than 24 minutes, at which point Debian will probably delete t开发者_开发知识库heir session (there's a cron job which runs every half hour deleting all sessions which have been unmodified for over 24 minutes).
  • If they close the browser, as their session cookie with an expiry time of 0 will be deleted.

The checks for seeing whether a user is logged in are:

  • They have a valid session (checked by seeing whether we can access $zsession->user_id).
  • There is a row in the sessions table which has a matching user ID and session ID, and this was last updated less than an hour ago. We delete this row on logout so that even if the session still exists on disk, no one can access that account without logging in.

Can anyone suggest other things I can try?

Edit: Some additional things I have tried based on comments left:

  • Setting session.cookie_domain: This seems to have very odd behaviour in PHP. If I do not set this variable and leave it as the default of '' (empty string), then a request for www.domain.com will produce a cookie of www.domain.com. However, if I set cookie_domain to 'www.domain.com', the domain for the cookie is '.www.domain.com' (notice leading dot, which means valid for everything below www.domain.com, e.g. subsite.www.domain.com).
  • Setting session.cookie_lifetime: PHP does not seem to update the expiry time on each request, so if I set cookie_lifetime to 3600 the cookie will expire one hour after the user first visits the site, even if they login and constantly use it.

Edit 2: Based on other things people have asked:

  • The site is hosted in a datacentre, on a separate VLAN. No one accessing the site is on the same network as the site.
  • There is no IP authentication used, nor is the IP address of the client used in any part of the session process (e.g. we don't attach the session to an IP address and block the user if their next request comes from a different IP).


Debian deletes sessions through a cron job instead of using PHP's garbage collector

That's very odd - and what is the code that runs in the cron job?

We delete this row on logout

I suggest you keep this for, say, 2 days after the session has expired / is deleted (but flag it as dead at the point where you currently delete it). Also, start logging the session id in your webserver logs.


In the end, the answer was to just scrap sessions and write my own very simple cookie code which differs from sessions in the following ways:

  1. Stores a hash (bit like a session ID) in the database rather than in files.
  2. Sets the cookie to expire in 3600 seconds from now (updated on each page) instead of 0 seconds (the latter seemed to cause problems for IE users, although I could never replicate it).
  3. Only sends the cookie header when the user logs in or is logged in.

It's not an ideal situation as there's some reinventing the wheel going on, but my small solution seems to work where PHP sessions didn't, and having a working site is the most important thing.


I think you count change the value of

session.gc_maxlifetime 

I also faced the same problem. I spent lots of time on it then i ask my web service provider and as he got the permission he changed that vale. Now it works fine.


Are there other php applications running on the same system (under different vhosts, for instance?)? Are they also saving sessions in /var/lib/php5?

If so, and one of those apps has a low session garbage-collection threshold, they will trash your app's session files.

I do a lot of ZF development, and if I'm using filesystem-based sessions, I stick them in application/data/session instead of the system default.


your session.gc_maxlifetime is set to 1440 ms which is only 1.44 seconds. shouldn't it be 1440000 ms = 24 minutes?


You could try setting session.use_only_cookies to a value of 1 and session.cookie_lifetime to a value of 1440 seconds.


is your site on different domains ? for example domain.com, www.domain.com, subdomain.domain.com ? if some pages are redirected to a different domain (www is considered a subdomain that is different) than the sessions will not work when the address changes

EDIT: You must reproduce the problem. Ask your clients what kind of browser do they use, what action they do until they are logedout, are they viewing the same ip for the site as you do? (that is you are both in external networks or both in the same network with the site)

When you manage to find the problem, check the request/response headers when it the session works, and also when it does not work and then compare.


In the end I opted to send a Set-Cookie header on every page request, similar to what FlyBy suggested in one of the comments. The relevant code/logic now is (assuming session_start() has already been called):

$this->session_name = session_name();
$update_cookie = isset($_COOKIE[$this->session_name]); // Check if cookie already set, as PHP will send the first Set-Cookie when the session is started
$this->logged_in = $this->checkSession(); // Function which checks whether a valid (i.e. not timed-out) session row exists in the DB
if ($this->logged_in) {
  $this->updateSession(); // Update the session row to the current time

  if ($update_cookie) {
    // Update the cookie expiry only if it existed before the login check
    setcookie($this->session_name, $_COOKIE[$this->session_name], $this->time + 3600, '/');
  }
}

I'm not sure why this worked, but I haven't had any further complaints and the number of logins has dropped drastically (no longer are there several logins in the logs for the same user within a few minutes of each other).

However, I will probably rewrite the code at some point to just use a database row and a cookie on the client, because the session functionality in PHP has so many variables that it's extremely difficult to work out what is causing the problem, and the session cookie handling is slightly different to how normal cookies are handled. In particular, you have to be careful with the setcookie function, because the default path for session cookies started by PHP is '/', but the default for setcookie is the current directory path, and these will not necessarily be the same.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜