PHP: ob_end_flush() end session - php

It seems that ob_end_flush() causes implicit session closing. Is this intended behavior? I suppose the answer is 'yes', but can I somehow prevent it?
Here's description of the problem I've encountered. I'm using framework that uses custom session handler, I don't think it matters in this case, but just for information. The actual problem occurs when at some point in framework code ob_end_flush() is called. By logging custom session handler calls I notice that session write and close called in this case. It's so implicit that I cannot even debug it with Zend Debugger, only log it with error_log. So, after ob_end_flush() interaction with session still continued (I know, that may be wrong, but still this is the case) and in that situation, when previous session is already closed, new session with new id is started. New session has new id because cookie is not set at the moment. So, as the result, I got two separate sessions with two different ids.
The best solution for me would probably be disabling that implicit ob_end_flush() behavior, but I will accept any answer.
Thanks in advance.

While a slightly indirect answer, it's common practice (and sometimes recommended practice) to start an output buffer with the purpose of buffering the entire request. Doing so allows you to continue to use session and/or set headers (including redirects) long after you start "outputting" content. This should take care of your implicit session close issue. Just add an ob_start() call to the top of your bootstrap.

Related

Is there some kind of performance (or other) punishment for using session_start() but not really using the session super-array?

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!

Codeigniter 3 upgrade session lock causing issues

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!

Correct management of php session for redirection

In my PHP application, I heavily use sessions to save information during redirection:
Saving information on category, so the correct tab and/or form is displayed after a redirection
Saving information when you are logged in to access some functionalities
Saving information on errors and warnings so they are displayed once all back-end operations are done and you are redirected to the main dashboard
In implementing that I called session_start in about 40 scripts, but outside of occasionally calling unset() on a $_SESSION variable I don't close the session in any way.
Now, while I was investigating performance issues on my server, it came to my attention that an underuse of session_write_close() might be the issue (even if the question was on an apache linux and my web app is hosted on a wamp server) and that in the end, even if it is not the cause, it might be sensible to try to close the session as soon and as often as I can.
I'm wondering how do I identify the points where I can safely destroy/restart the session? And what would be the best way to do that?
Keep in mind I want the number of opened session not being an issue, so I want to be sure to keep it at a minimum.
Typically we do not close session unless it is very important since session would be closed as soon as browser is closed.
Due to lack of knowledge of your application we are not in position to comment on exact need to close session in script. Considering the events you described it might be better idea to use cookies in certain situation like "Saving information when you are logged in to access some functionalities". Regarding error and warnings; you may like to remove some session variables after you have displayed messages on dashboard page.
For "Saving information on category, so the correct tab and/or form is displayed after a redirection" you can consider using parameters in URL to show desired tabs instead of using session variable.
In case you plan to use session_write_close() then in that case you would need to do session_start if you need access to session in the same script a second time. Use of session_write_close() is only to resolve locking of session in case two scripts try to access same session at same time. So you may like to describe if it is the case with your application or not.

When and where should I use session_start?

Exactly when and where should I use session_start() in PHP?
For example, say I have a login script that sets a session variable to tell whether or not the user is logged in. Must I then put the session_start() at the top of the script, or only right before I actually set the session variable if the login was successful?
<?php
// session_start(); here?
if (login($username, $password)) {
// session_start(); or here?
$_SESSION["username"] = $username;
}
?>
Another case is this, according to w3schools
Note: The session_start() function must be the very first thing in your document. Before any HTML tags.
As others have said, the absolute requirements of what you must do are:
You must run session_start before you read or write to $_SESSION (otherwise it will just be an ordinary array and not saved anywhere).
You must not run session_start twice during a single script execution (page load) unless you use session_write_close to close it in between.
There is an extra rule that technically has exceptions, but is best treated as absolute:
Do not start the session after you have written any output (echo, HTML outside PHP blocks, etc), because PHP may not be able to send cookies to the browser if the server has already started sending the content.
There are two reasons you might want to avoid starting the session:
PHP locks the session when you open it to avoid two processes writing conflicting data into it, so if you have several requests happening at once, you want to avoid them waiting for each other unless they really need to. For instance, if you're responding to an AJAX request, and don't need any data from the session, don't open it.
As mentioned by symcbean, there is some cost to creating a new session, so if your site is busy with either legitimate or malicious traffic, you might want to serve some landing pages or error messages without starting it at all.
After that, it becomes a matter of style and architecture, but the rule of thumb that covers most of the above is "as soon as possible, if you're sure the page needs it".
Unless you have output buffering enabled, the session_start() must come before anything other than headers are sent to the browser (as it sets a cookie in the header).
It must come before you attempt to reference the $_SESSION data.
In your example there are no html tags being output before either instance - so both would work.
There some cost to opening a session, so if you are doing additional, non-session based validation of the request, then deferring session_start() till these checks have passed does give you a bit more resillience against DOS attacks.
Starting the session at the top of the page is most of the times the best. But if you don't need the session for the whole document/code, you could always put it, as in this example, after the if() clause.
The session_start() function can go anywhere in your code. You should just place it at the beginning of your document for consistency and call it a day. If you have a separate database or config file you are including on all your login/database driven pages, you should place it in there so you don't have to recode it into every page.

How do I silently disable PHP sessions using php.ini?

I'd like to completely disable PHP sessions, but transparently to code.
That is, make it so that session_start() returns successfully, and $_SESSION exists, and works within a single request in the sense that a single request can read/write from that global, but then that data is thrown away at the end of the request, no session variable is passed or cookie'ed, PHP doesn't attempt to store it on disk or anywhere else, etc..
I read in one place that using the file session-handler with a gc_lifetime of 0 works, but elsewhere saying that doesn't work. And I wonder whether there's a no-op "handler" or similar thing that's the best/correct way to do this.
I prefer a solution using only php.ini, but an in-PHP solution is acceptable if that's the only way?
how about using auto_append_file directive in your php.ini file:
http://php.net/manual/en/ini.core.php#ini.auto-append-file
then adding the kill code session_destroy() \unset in the append file
PHP allows you to plugin a SessionHandler which is responsible for storing and loading the sessions. You can disable session retention by making all of the session handler functions NOPs. Users will still receive a session cookie (because PHP doesn't know any different), but that may be acceptable, depending on your use case.
Use auto_prepend_file to set up the session handling before any code is run that makes use of sessions.

Categories