I want to secure a little bit my application, especially I want to improve the way how sessions are handled. So, at this moment I know several facts:
session_regenerate_id(false) does not destroy old session
session_regenerate_id(true) destroys old session. With ordinary page reloads there is nothing wrong with using session_regenerate_id(true).
However when making dozens concurrent AJAX requests there may be a
problem which results in an error message object destruction failed.
So, there is nothing left to do, then to use
session_regenerate_id(false) in AJAX request.
But what is needed, is to somehow mark previous outdated sessions, which become outdated as a result of invoking session_regenerate_id(false), as "zombie" sessions that will somehow be destroyed and not litter the sessions folder.
I need some practical advice on how to implement this.
All session cleanup, including those with regenerated IDs, is handled by PHP's session garbage collector. There is nothing special needed when calling session_regenerate_id(false) to remove old sessions from storage.
PHP's settings for session.gc_probability, session.gc_divisor, and session.gc_maxlifetime apply.
You can also run your own session storage cleanup based on last access time.
Related
I've always wondered why PHP makes you manually session_start() in order to gain access to the immensely useful $_SESSION "super-array".
It strikes me that this might be causing a lot of stress on the server, but not really make a difference in practice unless you have an extreme amount of users.
I don't really see why it would cause such a strain, though, if you don't use that array/mechanism. And if you do, you always want session_start() to have been called... It would really be nice to finally get this straightened out.
The manual doesn't offer any explanation: https://www.php.net/session_start
It's not really a question of how much overhead it causes, as far as why sessions aren't started by default. There are tons of possible PHP applications that have nothing to do with session variables (including any CLI use!), and therefore, on principle it shouldn't be automatically started. Establishing an idle database connection ("immensely useful!") also doesn't create silly overhead. It's still not done by default. Resources should be available, but uninstantiated.
The main performance impact caused by starting a session, with or without ever using it, basically involves (quoting from the manual on session_start()):
PHP will call the open and read session save handlers. ... The read callback will retrieve any existing session data (stored in a special serialized format) and will be unserialized and used to automatically populate the $_SESSION superglobal when the read callback returns the saved session data back to PHP session handling.
This typically means disk access to look up and read the serialized session data. Even if it's empty or non-existent. (It will not be created until $_SESSION variables are used; but you won't know if it's there without trying!) Also: your session ID is typically stored in a cookie. Want session? Make a cookie, take a cookie, pass a cookie, read a cookie, etc. pass/read on each page load. Unnecessary baking and trading, and we'd rather avoid redundant HTTP traffic.
Aside that, there's a strange and wonderful thing called session locking that can make you scractczch your head a lot and wonder why you can't load long-running scripts on your site in two tabs simultaneously, even when you've double-damned-configured your Apache, MySQL and the rest to handle concurrent connections and/or space alien armadas on steroids. Without session locking, you could. (Alas, debugging long-running scripts with sessions on!)
Significantly, this will haunt you with concurrent AJAX requests to PHP scripts with sessions; they'll be sequentially processed instead. There are ways to overcome session locking delays, but the default behavior blocks parallel execution, and requests are queued, and it's rather annoying but a necessary evil to prevent race conditions and session corruption (read more).
So much for the obvious performance/quirks side. In terms of customizing how things work, there are functions that may be called before session_start(), such as session_name() (for named sessions). The session_start function itself (as of PHP 7) takes an optional array of session parameters which you couldn't use, were the session started by default.
If you look at the link above, you'll notice that there is in fact a session auto-start option in php.ini session configuration:
session.auto_start boolean
session.auto_start specifies whether the session module starts a session automatically on request startup. Defaults to 0 (disabled).
There are some related cautions in PHP Intro to Sessions on the auto_start option:
Caution If you turn on session.auto_start then the only way to put objects into your sessions is to load its class definition using auto_prepend_file in which you load the class definition else you will have to serialize() your object and unserialize() it afterwards.
If you are certain that you always want to use sessions, the simplest move would be to create a bootup file that you require at the beginning of files that use sessions; add the path to the file into your include path; and then simply <?php require 'sessions.php' before your main code begins.
The session bootup file could also have some of your own session-handling functions, etc. relevant standard material. This route would give you more freedom than the auto-start option, plus a way to implement other options and functionality across all your session-using code. You shouldn't rely on the auto-start in any case, in case you ever want to create code that can be easily deployed into environments with default PHP configuration!
Is it possible to check if a certain session ID exists without starting a new one?
The reasons I'm asking this are:
If every user not logged in visiting the site requires session_start(), soon the session folder will be bloated with unnecessary files (even with garbage collector cleaning up expired sessions).
Of course this can be mitigated by destroying the sessions of such users. However, in this case, it seems there would be an overhead of creating and destroying files (unless PHP is smart enough to cancel these operations). Is this a bad practice?
If the user is just a guest and doesn't require a session, there is no need to create one for him.
Ideal Solutions:
if( session_exist($_COOKIE['PHPSESSID']) )
{
session_start();
}
Or:
session_start(RESUME_ONLY);
Follow-up
There is a PHP package that explains much better the aforementioned issue. Briefly, some interesting excerpts are:
Resuming A Session
[...]
You could start a new session yourself to repopulate $_SESSION, but that will incur a performance overhead if you don't actually need the session data. Similarly, there may be no need to start a session when there was no session previously (and thus no data to repopulate into $_SESSION). What we need is a way to start a session if one was started previously, but avoid starting a session if none was started previously.
[...]
If the cookie is not present, it will not start a session, and return to the calling code. This avoids starting a session when there is no $_SESSION data to be populated.
Source: Aura.Auth
Notes:
This question was initially posted as Checking for PHP session without starting one?.
However, IMO, none of the answers addressed properly the issue.
I think you are trying to solve the wrong problem here. To answer your question, no, you must use session_start before preforming session operations. You could also destroy the session if it isn't needed in the same request with session_destroy.
You can work around the session_start problem with some clever session storage options, but I think that is just the wrong answer to the problem you have. The size of sessions stored on the FS is really negligible when your servers installation footprint is at least 8GB. Furthermore, the default session storage location is /tmp which is commonly setup as a ramfs to avoid any disk overhead. If you are running into the limitations of FS session storage, you probably need to use a different SessionHandler like memcached, not worrying about if you need to start a session. On a related note, stop prematurely optimizing your code. Session storage is not going to be an issue until you need more than one server. In reality there are going to be many more database queries that would benefit from an additional index than obscure optimizations like this one.
We've recently upgraded an old Codeigniter app from 2.1.0 to 3.1.9, and everything has gone smoothly. Except, that the new session locking is causing issues and I'm wondering the proper way to fix it.
The app uses AJAX heavily, however most of the AJAX calls don't write to the session and don't seem to break it.
Here is an example of the issue: there is a GUI with checkboxes, and when the input is changed (a checkbox is checked or unchecked) an AJAX call was made. On the other end of that AJAX call which boxes were checked were written to session so that they would be remembered from visit to visit. However, if you checked/unchecked multiple boxes causing multiple AJAX calls to go out, you would end up getting logged out. Similar behavior has been discovered around the app, all where session writes are happening.
I've tried implementing session_write_close() as suggested by the Codeigniter documentation but that only half worked in some spots, and caused more issues in area where there were no issues before. The app has a few endpoints that do all the work and all work flows share, so fixing the endpoint where the session writes are happening with session_write_close() breaks other script calls when they continue to need the session.
The short term solution I've come up with is to debounce the AJAX calls (which helps but doesn't solve the problem by itself) and to disable inputs until the AJAX call has finished.
Is there a better long term solution? Ultimately this app is being phased out, so spending a long time rewriting it isn't feasible.
The only long-term solution is to properly use session_write_close().
As you undoubtedly understand, session data is locked so only one script at any time can write to the session's persistent datastore. Session locking prevents hard to troubleshoot concurrency bugs and is more secure.
Without seeing your implementation it's really hard, er... impossible to offer any precise advice. Here are some things to consider that might help sort out the mess.
Either do ALL or NONE of the session writes in the AJAX response functions. (By "AJAX response function" I mean the PHP controller/method value of the AJAX url.)
With the ALL approach call session_write_close() in the "main" script before making any AJAX requests. Keep in mind that $_SESSION is not affected by session_write_close(). All $_SESSION items in the main script will remain accessible so you can reliably read the values. However, changes made to $_SESSION will not be written because, as far as PHP is concerned, the session is closed. But that's only true for the script that calls session_write_close().
With the NONE approach you may still need to read session data. In that case it would be wise to have the AJAX response functions call session_write_close as soon as possible to minimize the time concurrent requests are blocked. The call is more important for functions that require significant time to execute. If the script execution time is short then the explicit call to session_write_close() is not needed. If at all possible, i.e. no need to read session data, then not loading the session class might result in cleaner code. It would definitely eliminate any chance of concurrent request blocking.
Don't try to test session behavior by using multiple tabs to the same app on the same browser.
Consider using $config['sess_time_to_update'] = 0; and then explicitly call $this->sess_regenerate((bool) config_item('sess_regenerate_destroy')); when and where it makes sense that the session id needs to be changed, i.e. right after login; right after a redirect to a "sensitive" page; etc.
What follows next is offered with a large amount of trepidation. I've tested this using the "files" driver, but not extensively. So, buyer beware.
I found that it is possible to "re-start" a session by calling the PHP function session_start() after session_write_close() has been used. CodeIgniter will open and read the session datastore and rebuild the $_SESSION superglobal. It's now possible to change session data and it will be written when script execution ends - or with another call to session_write_close().
This makes sense because session_write_close() does not "do" anything to the CodeIgniter session object. The class is still instantiated and configured. CodeIgniter's custom SessionHandlerInterface is used to open, read, and write session data after session_start() is called.
Maybe this apparent functionality can be used to solve your problems. In case I wasn't clear earlier - use at your own risk!
My application is a full AJAX web page using Codeigniter Framework and memcached session handler.
Sometimes, it sends a lot of asynchronous calls and if session has to regenerate its ID (to avoid session fixation security issue), the session cookie is not renewed fast enough and some AJAX calls fail due to session id expired.
Here is a schematic picture I made to show clearly the problem :
I walked across the similar threads (for example this one) but the answers doesn't really solve my problem, I can't disable the security as there is only AJAX calls in my application.
Nevertheless, I have an Idea and I would like an opinion before hacking into the Codeigniter session handler classes :
The idea is to manage 2 simultaneous session Ids for a while, for example 30 seconds. This would be a maximum request execution time. Therefore, after session regeneration, the server would still accept the previous session ID, and switch to session to the new one.
Using the same picture that would give something like this :
First of all, your proposed solution is quite reasonable. In fact, the people at OSWAP advise just that:
The web application can implement an additional renewal timeout after which the session ID is automatically renewed. (...) The previous session ID value would still be valid for some time,
accommodating a safety interval, before the client is aware of the new
ID and starts using it. At that time, when the client switches to the
new ID inside the current session, the application invalidates the
previous ID.
Unfortunately this cannot be implemented with PHP's standard session management (or I don't know how to do that). Nevertheless, implementing this behaviour in a custom session driver 1 should not pose any serious problem.
I am now going to make a bold statement: the whole idea of regenerating the session ID periodically, is broken. Now don't get me wrong, regenerating the session ID on login (or more accurately, as OSWAP put it, on "privilege level change") is indeed a very good defense against session fixation.
But regenerating session IDs regularly poses more problems than it solves: during the interval when the two sessions co-exist, they must be synchronised or else one runs the risk loosing information from the expiring session.
There are better (and easier) defenses against simple session theft: use SSL (HTTPS). Periodic session renewal should be regarded as the poor man's workaround to this attack vector.
1 link to the standard PHP way
your problem seems to be less with the actual speed of the requests (though it is a contributing factor) but more with concurrency.
If i understand right, your javascript application makes many (async) ajax calls - fast (presumably in bursts)- and sometimes some of them fail due to session invalidation due to what you think is speed of requests issue.
Well i think that the problem is that you actually have several concurrent requests to the server, while the first one has its session renewed the other essentially cannot see it because the request is already made and waits to be processed by the server.
This problem will of course manifest itself only when doing several requests for the same user simultaneously.
Now The real question here - what in your application business logic demands for this?
It looks to me that you are trying to find a technical solution to a 'business' problem. What i mean is that either you've mis-interpreted your requirements, or the requirements are just not that well thought/specified.
I would advice you to try some of the following:
ask yourself if these multiple simultaneous requests can be combined to one
look deeply into the requirements and try to find the real reason why you do what you do, maybe there is no real business reason for this
every time before you fire the series of requests fire a 'refresh' ajax request to get the new session, and only on success proceed with all the other requests
Hope some of what i've wrote help to guide you to solution.
Good luck
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.