Jump to content

[SOLVED] Session Madness!


bryan52803

Recommended Posts

My website has been plagued by a session problem that I've been unable to resolve. Let me quickly rundown my setup:

 

* All sessions are stored/manipulated using user defined functions so that it stores everything in a MySQL table

* I force the use of cookies only

* Here is a clip of my php.ini that is relative to the topic:

session.bug_compat_42 = 0
session.gc_divisor = 100
session.hash_bits_per_character = 5
session.use_cookies = 1
session.use_only_cookies = 1
session.name = COOKIENAME //censored for security purposes
session.auto_start = 0
session.cookie_lifetime = 3600
session.cookie_path = /
session.cookie_domain = www.mydomain.com //censored for security purposes
session.gc_maxlifetime = 3600

 

* Now for the session handling code:

<?php

function mysql_session_open($session_path, $session_name)
{
mysql_pconnect("localhost", "USER", "PASS") or die(mysql_error()); //censored for security purposes
mysql_select_db("DBNAME") or die (mysql_error());  //censored for security purposes
return true;
}
function mysql_session_close()
{
return true;
}
function mysql_session_select($SID)
{
$query = "SELECT variables FROM sessions WHERE SID='".mysql_real_escape_string($SID)."' AND expiration > ".time();
$result = mysql_query($query) or die(mysql_error());
if(mysql_num_rows($result))
{
	$row = mysql_fetch_assoc($result);
	$value = $row['variables'];
	return $value;
}
else
{
	return "";
}
}
function mysql_session_write($SID, $value)
{
$value = mysql_real_escape_string($value);
$lifetime = get_cfg_var("session.gc_maxlifetime");
$expiration = time() + $lifetime;

$query = "INSERT INTO sessions VALUES('".mysql_real_escape_string($SID)."', '$expiration', '$value')";
$result = mysql_query($query);
if(!$result)
{
	$query = "UPDATE sessions SET expiration='$expiration', variables='$value' WHERE SID = '".mysql_real_escape_string($SID)."' AND expiration > ".time();
	$result = mysql_query($query) or die(mysql_error());
}
return true;
}
function mysql_session_destroy($SID)
{
$query = "DELETE FROM sessions WHERE SID='".mysql_real_escape_string($SID)."' LIMIT 1";
$result = mysql_query($query);
return true;
}
function mysql_session_garbage_collect($lifetime)
{
$query = "DELETE FROM sessions WHERE expiration <= ".time();
$result = mysql_query($query);
return mysql_affected_rows($result);
}

session_set_save_handler("mysql_session_open", "mysql_session_close", "mysql_session_select", "mysql_session_write", "mysql_session_destroy", "mysql_session_garbage_collect");

?>

 

* And finally what prefaces every page:

<?php
INCLUDE "session.php";
session_start(); ?>

 

Now my problem. After viewing my webpage, and deciding to login to make a post, everything works fine, but alot of times when I go to submit a post, or any interim action that requires me to be logged in, the session data is lost and I'm prompted to login again.

 

Here's what I think is happening. My session as stored in mysql is being persisted whenever session data is written (as can be seen in my session write handler). So the session persists correctly in the database. Because forced cookies is enabled, a cookie is generated with the start of every page, but only to the lifetime of 3600. So perhaps when making a post, and dealing with session data, the session is persisted in mysql, but when I go to perform an action that requires me to be logged in, the cookie, which started before I logged in when browsing, expires, and thus the SID is not properly channeled and is lost. This forces my session handlers to create a new session, devoid of the data once stored.

 

So, that said, if this is indeed the case, how can I persist the cookie, or at least 'sync' the cookie expiration with the database expiration? This has been causing me problems for quite some time, and any help on the matter would be greatly appreciated. Thank you in advance for your time and consideration!

 

Bryan Paul

 

PS: In case it is of concern, here is my code to logout:

<?php
INCLUDE "session.php";
session_start();

setcookie("COOKIENAME", "", time()-3600, "/", "", FALSE, TRUE); //censored for security

session_destroy();
header('Location: index.php?logout');
exit;
?>

Link to comment
Share on other sites

session.cookie_domain = www.mydomain.com

 

Your cookie domain setting requires the www. If you are using any url's that goto mydomain.com (without the www.), then the session cookie does not match and won't be sent to the server. You should actually use .mydomain.com (with the leading dot) to match all host/sub-domain names.

 

The cookie lifetime is only the amount of time after the browser is closed. As long as the browser is not closed, then the cookie is kept in the browser's cookie cache and you can navigate anywhere, even off site, and the session cookie will be sent to the server (providing the domain/path settings match the current url.)

Link to comment
Share on other sites

Interesting. Thank you for the input. I've corrected the domain; I did not even think of that. However, I do not, at this time, use any subdomains so that is not where the issue lies.

 

But what you mentioned about the cookie expiration means that, if this is the case, that the problem is somewhere other than the cookies perhaps. Now I'm really lost as to what is happening  :-\. Maybe, for whatever reason, the expiration is not being updated in the database as it should. I'll double check the syntax and make sure, but then an error should have occurred, and the output would have been affected, as I have error outputting turned on currently.

 

Thanks again.

 

Bryan

Link to comment
Share on other sites

Ok now for a bizarre new twist...

 

My server time is GMT - 8.

 

I noticed when quickly checking the session expiration after creating or updating a session, it set it not equal to the current time plus 3600seconds (1 hour), but it set it equal to THE CURRENT TIME. This is perplexing to me. How could the time() function in my session handlers be different from time() called anywhere else?

 

Setting the expiration to time() + 3600 + 3600 finally yielded the correct session expiration. This makes no sense to me...

 

When I make a new post, I call the time() function to insert the timestamp into mysql. This yields the proper timezone of GMT - 8.

 

However, calling time() in the session handler, it's an hour behind, GMT - 9. ARGH! Is there any proper explanation for this behavior, and a way I can get everything on the same page!??!

 

-Very Confused

Link to comment
Share on other sites

You should store the current time in the database and then do any session.gc_maxlifetime checking in the mysql_session_garbage_collect routine, that is why that function receives the $lifetime as a parameter. The way it is now, if you change the session.gc_maxlifetime setting, it won't affect any existing sessions.

 

One thing that is of some concern, you are calling the timestamp you are storing the "session expiration". The purpose of the garbage collection function is only to delete old session data files/records. It is not for the purpose of ending sessions. The garbage collection is truly a random function using the session.gc_probability/session.gc_divisor. Using the garbage collection function for any other purpose or setting the session.gc_probability so that GC runs on each session start will both prevent you from using sessions normally and will create an unnecessary server load.

Link to comment
Share on other sites

Hmm. I see your first point. However, I'm not running the GC function upon every session start; I'm not sure what you're exactly referring to here.

 

Back to the first point, I took your advice. Now it inserts the current time into the database, and no longer enforces an expiration at a PHP level. So the only way the session is ended is as follows:

 

* The user logs out calling the mysql_session_destroy function

* The cookie is destroyed and thus the SID is no longer persisted/channeled and a new session is created. This will happen 3600s after they close their browser, correct?

* OR the session time in the database, when it was created, is lower than the current time plus the gc_lifetime variable (1 hour).

 

Let me play around with this configuration and see if it works better. As a side note, I feel like most of my woes are extending from a lack of knowledge on the session_save_handler function. Is there a good set of documentation on this function? I have yet to come across one that really explains session handling in detail.

 

Bryan

 

EDIT: It seems like this might be my best bet; and as a side note, ignore the time difference thing. It appears the timestamp converter I was using online didn't take into account daylights savings time. Ugh, sometimes I hate my life....

Link to comment
Share on other sites

While you can delete the session cookie (your setcookie() code) to log someone out, you should not rely on that. Someone can simply make a copy of the cookie and put it back if they see you are changing it or deleting it to log them out.

 

You should only rely on server side data to indicate if someone is logged in or logged out.

Link to comment
Share on other sites

I'm well aware of that, which is why session_destroy() is called. Thank you.

 

That said, I'm inclined to disagree with you. After several experiments, I noticed the cookie does NOT persist 3600s after browser closure as you indicated, but rather 3600s after creation, as I originally stated.

 

Which leads me back to my original point and question: how do I persist a cookie to match a persisting session?

 

Maybe it's firefox, but I'm positive this is what is happening:

 

1. User browses my site

2. Since cookies are forced, a cookie is created immediately at the first viewing of a page.

3. After 50min of browsing, the user decides to post a comment.

4. By logging in and writing session data, the expiration in the session (the mysql database) is augmented, thus restarting the expiration back to 1 hour from the present time.

5. If the user takes 15 minutes to write a comment and press submit, the cookie expires (being set to 60min).

6. A new session is created with NO data, the user is prompted to login.

7. Though the session data exists in the database with a proper, updated expiration time, the SID was not propagated through the cookie and thus all data is seemingly lost.

 

Does anyone have a solution to this problem? Thank you for your time and consideration.

 

Bryan

Link to comment
Share on other sites

SOLVED:

 

To update the cookie, one needs to set the cookie again with the SAME PARAMETERS but different time.

 

If cookies are automatic, its a such:

 

setcookie(session_name(), session_id(), time()+3600, "PATH","DOMAIN"); //Augment Cookie expiration by 1 hour (3600s)

 

Where PATH is path specified in your php.ini, and DOMAIN is the domain specified in your php.ini. Leaving any of these parameters blank or NULL as old documentation has suggested, the cookie will not update. I believe this is a security feature of modern browsers.

 

So two lessons learned here:

 

* Cookies expire on time, not 3600s after browser closure

* To change the time of a cookie (to include time()-3600 to delete), setcookie must be called with ALL the same parameters except the different time.

 

Bryan

Link to comment
Share on other sites

This thread is more than a year old. Please don't revive it unless you have something important to add.

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.