You should implement a session timeout of your own. Both options mentioned by others (session.gc_maxlifetime and session.cookie_lifetime) are not reliable. I'll explain the reasons for that.
First:
session.gc_maxlifetime
session.gc_maxlifetime specifies the number of seconds after which data will be seen as 'garbage' and cleaned up. Garbage collection occurs during session start.
But the garbage collector is only started with a probability of session.gc_probability divided by session.gc_divisor. And using the default values for those options (1 and 100 respectively), the chance is only at 1%.
Well, you could simply adjust these values so that the garbage collector is started more often. But when the garbage collector is started, it will check the validity for every registered session. And that is cost-intensive.
Furthermore, when using PHP's default session.save_handler files, the session data is stored in files in a path specified in session.save_path. With that session handler, the age of the session data is calculated on the file's last modification date and not the last access date:
Note: If you are using the default file-based session handler, your filesystem must keep track of access times (atime). Windows FAT does not so you will have to come up with another way to handle garbage collecting your session if you are stuck with a FAT filesystem or any other filesystem where atime tracking is not available. Since PHP 4.2.3 it has used mtime (modified date) instead of atime. So, you won't have problems with filesystems where atime tracking is not available.
So it additionally might occur that a session data file is deleted while the session itself is still considered as valid because the session data was not updated recently.
And second:
session.cookie_lifetime
session.cookie_lifetime specifies the lifetime of the cookie in seconds which is sent to the browser. […]
Yes, that's right. This only affects the cookie lifetime and the session itself may still be valid. But it's the server's task to invalidate a session, not the client. So this doesn't help anything. In fact, having session.cookie_lifetime set to 0
would make the session’s cookie a real session cookie that is only valid until the browser is closed.
Conclusion / best solution:
The best solution is to implement a session timeout of your own. Use a simple time stamp that denotes the time of the last activity (i.e. request) and update it with every request:
if (isset($_SESSION['LAST_ACTIVITY']) && (time() - $_SESSION['LAST_ACTIVITY'] > 1800)) {
// last request was more than 30 minutes ago
session_unset(); // unset $_SESSION variable for the run-time
session_destroy(); // destroy session data in storage
}
$_SESSION['LAST_ACTIVITY'] = time(); // update last activity time stamp
Updating the session data with every request also changes the session file's modification date so that the session is not removed by the garbage collector prematurely.
You can also use an additional time stamp to regenerate the session ID periodically to avoid attacks on sessions like session fixation:
if (!isset($_SESSION['CREATED'])) {
$_SESSION['CREATED'] = time();
} else if (time() - $_SESSION['CREATED'] > 1800) {
// session started more than 30 minutes ago
session_regenerate_id(true); // change session ID for the current session and invalidate old session ID
$_SESSION['CREATED'] = time(); // update creation time
}
Notes:
session.gc_maxlifetime
should be at least equal to the lifetime of this custom expiration handler (1800 in this example);
- if you want to expire the session after 30 minutes of activity instead of after 30 minutes since start, you'll also need to use
setcookie
with an expire of time()+60*30
to keep the session cookie active.
I would think you'll want to think about if things really belong in a session state. This is something I find myself doing every now and then and it's a nice strongly typed approach to the whole thing but you should be careful when putting things in the session context. Not everything should be there just because it belongs to some user.
in global.asax hook the OnSessionStart event
void OnSessionStart(...)
{
HttpContext.Current.Session.Add("__MySessionObject", new MySessionObject());
}
From anywhere in code where the HttpContext.Current property != null you can retrive that object. I do this with an extension method.
public static MySessionObject GetMySessionObject(this HttpContext current)
{
return current != null ? (MySessionObject)current.Session["__MySessionObject"] : null;
}
This way you can in code
void OnLoad(...)
{
var sessionObj = HttpContext.Current.GetMySessionObject();
// do something with 'sessionObj'
}
Best Answer
First of all I say that MS asp.net have lock in the core of "asp.net processing a page" the session, somewhere in the "webengine4.dll" dll for asp.net 4
And I say that from the view point of MS
asp.net act correct and lock the session this way
because asp.net can not know what type of information we keep on session so can make a correct "last write wins".Also correct is lock for the session the full page because that way is gives your a very important synchronizations of your program, that from experience now I say you need it for most of your actions. After I have replace the ms session with mine, on all of my actions I need to make global lock, or else I have problems with double inserts, double actions, etc.
I give an example of the issue that I recognize here. Session data are saved on SessionSateItemCollection that is a list of keys. When session reads or write this collection is do it all of them together. So let see this case.
we have tree variables on session, "VAR1", "VAR2", "VAR3"
Page 1.
getsession (actually get all data and place them on list)
session[VAR1] = "data1";
savesession()
result is VAR1=data1, VAR2=(last data of var2), VAR3=(last data of var3)
Page 2.
getsession (actually get all data and place them on list)
session[VAR2] = "data2";
savesession()
result is VAR1=(last data of var1), VAR2=data2, VAR3=(last data of var3)
Page 3.
getsession (actually get all data and place them on list)
session[VAR3] = "data3";
savesession()
result is VAR1=(last data of var1), VAR2=(last data of var2) VAR3="data3"
Note here that each page have a clone of the data, as he read them from the session medium (eg as they last readed from database)
If we let this 3 pages run with out locking, we do not have actually dirty read, nether "last write wins" - What we have here is "the last write destroy the others", because if you think it again, when VAR1 change, why the VAR2 and VAR3 stay the same ? what if you have change VAR2 somewhere else.
and for that reason we can not let this with out lock as it is.
And now imaging that with 20 variables... totally mess.
Possible solutions
Because of the multithread of asp.net of many pools, the only way to keep the same data across pools, and across computers is to have a common database, or a common to all process program like the asp.net State Service.
I select to have a common database as more easy than create a program for that propose.
Now if we connect a cookie that we make, to the user to the data user of the session, and we control the data using a totally custom database field that we know what we like to lock, what not, how to save or change, or compare and keep the last write wins data, we can make this work, and totally disable the asp.net session that is a generic session keeper made for all needs, but not made to handle special cases like this one.
Can asp.net make this work on the future release, yes he can by making an extra field called dirty, and on the session save data, make a merge of data using the dirty field, or something like that, but can not make this now work as it is - at least from what I have found until now.
Actually SessionStateItem have a dirty flag on properties, but did not make this merge on the end of save data. I will really love if some one else think for a solution to the existing ms asp.net session state keeper, I am write what I have found up to now, but this is not mean that for sure there is no way - I have not found it as it is.
Hope that all helps :)
Custom SessionStateModule
in this answer https://stackoverflow.com/a/3660837/159270 the James after write a custom module says: I still can't believe the custom implementation of ASP.Net Session locks the session for the whole request.