PHP sessions and race conditions - php

The PHP documenation for session_write_close says
...but as session data is locked to prevent concurrent writes only one script may operate on a session at any time.
Could somebody please elaborate on what this means? Does this mean that the actual session store is locked for writing? If so and I'm using a custom session handler that writes to a SQL database, how do things change? Are all subsequent requests for that session completely blocked until the lock is released?
I ask because I'm using a custom session handler that uses a table in a MySQL database, and I think I might be encountering a race condition when more than one request comes in at a time for a session. If the first request reads the session from the table, and a second request comes in before the first finishes, will the second request get blocked until the first completely finishes or calls session_write_close? If not, would it be possible for the second to overwrite whatever the first had just written if it had read before the first had written?
Also, if the first request is aborted after reading the session but before it writes, is session_write still called?

Related

Are session variables in shared memory?

If you are familiar with programing languages like VB.NET, you know there is something name Shared-Memory which is shared between different instance of application. My question is :Are session variables behave the same in PHP or not?
Assume this scenario:
I click twice on a link in my web-site (Both requests are sent with same headers, same cookies but in different times). $_SESSION['num'] is set to 0 and now:
On 12:00:00.01 first request is received by the server
On 12:00:00.03 first request starts its session
On 12:00:00.04 second request is received by the server
On 12:00:00.05 second request starts its session.
On 12:00:00.06 first process adds 10 to value of $_SESSION['num'].
On 12:00:00.07 second process adds 10 to value of $_SESSION['num'].
On 12:00:00.09 both processes are finished.
Now , there are two possible answers:$_SESSION['num'] is 10 or $_SESSION['num'] is 20. Which one is the answer?
The usual answer to your uestion is "what happened when you tested it?"
PHP will store your sessions wherever you tell it to store the sessions: files, shared memory, a database. By default it uses files. The session is retrieved from storage when you call session_start() it is written back to storage when you call session_write_close(), or the script exits.
If your session handler acquires a lock on the storage, then the second instance will be blocked until the first instance writes back the changes. The default files handler acquires locks.

How should I make a long PHP request via AJAX, periodically check for status updates, and close the script if the request cancels?

Part of the PHP web app I'm developing needs to do the following:
Make an AJAX request to a PHP script, which could potentially take from one second to one hour, and display the output on the page when finished.
Periodically update a loading bar on the web page, defined by a status variable in the long running PHP script.
Allow the long running PHP script to detect if the AJAX request is cancelled, so it can shut down properly and in a timely fashion.
My current solution:
client.php: Creates an AJAX request to request.php, followed by one request per second to status.php until the initial request is complete. Generates and passes along a unique identifier (uid) in case multiple instances of the app are running.
request.php: Each time progress is made, saves the current progress percentage to $_SESSION["progressBar"][uid]. (It must run session_start() and session_write_close() each time.) When finished, returns the data that client.php needs.
status.php: Runs session_start(), returns $_SESSION["progressBar"][uid], and runs session_write_close().
Where it falls short:
My solution fulfills my first two requirements. For the third, I would like to use connection_aborted() in request.php to know if the request is cancelled. BUT, the docs say:
PHP will not detect that the user has aborted the connection until an attempt is made to send information to the client. Simply using an echo statement does not guarantee that information is sent, see flush().
I could simply give meaningless output, but PHP must send a cookie every time I call session_start(). I want to use the same session, BUT the docs say:
When using session cookies, specifying an id for session_id() will always send a new cookie when session_start() is called, regardless of if the current session id is identical to the one being set.
My ideas for solutions, none of which I'm happy with:
A status database, or writing to temp files, or a task management system. This just seems more complicated than what I need!
A custom session handler. This is basically the same as the above solution.
Stream both progress data and result data in one request. This solves everything, but I would essentially be re-implementing AJAX. That can't be right.
Please tell me I'm missing something! Why doesn't PHP know immediately when a connection terminates? Why must PHP resend the cookie, even when it is exactly the same? An answer to any of these questions will be a big help!
My sincere thanks.
Why not set a second session variable, consisting of the unique request identifier and an access timestamp, from status.php.
If the client is closed it stops getting updates from status.php and the session variable stops being updated, which triggers a clean close in request.php if the variable isn't updated in a certain amount of time.

Can't have several threads per session

I am buidling some webapp and have implemented long-polling (and a command queue in my db) so my server can send commands to my cleint asynchronously, etc. The commands are encoded into json and sent over ajax calls for the client to server, and via long-polling for the server to client way.
Everything was working just fine, until I included my "Authentication module" in the ajax.php file. This module wraps the session stuff and calls session_start().
The problem is that, my long polling routine can wait up to 21 seconds before comming back to the client. During this time, the server won't run anything from the same session. It's instead executed right after the long polling ajax call returned.
I understand there's probably a restriction of only 1 thread per session at a time, and that the requests are queued up.
Now here's the question : What is the best way to address this? Is there a setting to allow several threads per sessions (3 would be fine, in my case). Or should I just send tell the client what is his SessionID (i have some sessions table in my db, to track which user is connected to which session(s)). The client could then send it along with any ajax calls so authentication module could be bypassed.
On the later option, iam afraid it open's up a bunch of security problems because of eventual session spoofing. I would need to send a "random string" to each session, to make sure you can't spoof too easily, but even then, it's not perfect...
Thanks for your awnsers :)
Nicolas Gauthier
It's a well known issue/fact that PHP locks session files for the duration of their usage in order to prevent race conditions.
If you take a look at the PHP source code, (ext/session/mod_files.c) you can see that the ps_files_open function locks the session file, and ps_files_close unlocks it.
If you call session_start() right at the beginning of your long-running script, and do not explicitly close the session file, it will be locked until the script terminates, where PHP will release all file locks during script shutdown.
While you are not using the session, you should call session_write_close to flush the session data to disk, and release the lock so that your other "threads" can read the data.
I'm sure you can imagine what would happen if the file was not locked.
T1: Open Session
T2: Open Session
...
T2: Write Data
T1: Write Data
The data written by thread 2 will be completely overwritten by thread 1, and at the same time, any data that thread 1 wanted to write out, was not available to thread 2.

Session_set_save_handler close/gc

I need to perform an action after a session times out. However I have no clue how to trigger that action without an incoming request. An event handler/a listener/a timer would suit perfect but since PHP does not support such a thing it is really difficult to accomplish what I want.
After lot of searching I bumped into session_set_save_handler but I still cannot fully understand how this method works. If I write code that triggers the action inside the close method when it will be executed?
I also need to keep the current session solution as it is and I wonder what the ramifications of using session_set_save_handler in conjunction with that will be? (My current solution sets a session cookie, assigns a name to the session and starts the session, when the user logs out or a request is done after the session timeout (that value is set in a configuration file) the session is completely destroyed)
Regards!
This is somewhat difficult to do in php, AFAIK.
But you can try by making a passive session handler:
Store session ids associated with a timestamp.
Each time the user associated with certain id makes a request, refresh it's timestamp.
You can detect defunct sessions by comparing the system's current, and each session id's timestamps. The ones that differ above a given treshold (say 30min), are assumed to have passed away. Then you can execute your own save handlers for these session ids.
This won't work if the session end handler needs to be executed inmediately, as this process is executed each time when a request arrives (from any user), so it will depend directly on the website's traffic flow.
But you can also solve it by setting cron jobs each 15min or so. Depending on how expensive your save handlers are, seems an acceptable periodicity.
PHP needs to run in some way for PHP code to execute. Either through a user request or a cronjob.
A Session is saved to the locale storage when a PHP request finishes executing or when session_write_close() is called
This session_set_save_handler() allows you to write your own save handler.

Does every access to $_SESSION immediately involves an i/o with the file system?

Every time I access data in $_SESSION, Does it immediately update the session file on the disk, or just once when the process goes down? Or every n bytes of data change (flush)?
This question is not necessarily about the specific file session handler, but every handler. (Does every touch in session immediately invoke an I/O of any kind, beside the storing of a normal variable in memory).
As Matt wrote, it writes at the end of script execution by default. You can read about it here in session_write_close()
Session data is usually stored after
your script terminated without the
need to call session_write_close(),
but as session data is locked to
prevent concurrent writes only one
script may operate on a session at any
time. When using framesets together
with sessions you will experience the
frames loading one by one due to this
locking. You can reduce the time
needed to load all the frames by
ending the session as soon as all
changes to session variables are done.
It writes it and the end of the process on my setup. I made a new _ session_ write_method:
public function _session_write_method($id, $sess_data) {
var_dump(file_put_contents('/var/www/public_html/testing.txt', serialize($sess_data)));
return(true);
}
and then:
$_SESSION['foo'] = 'bar';
while(true)
I executed the script,waited a few seconds and then ran 'sudo kill' on the process id. It did not write the serialized data to the file. I ran it again without the infinite loop and I got: int(22) at the very bottom of the page and testing.txt was successfully written to and contained: s:14:"foo|s:3:"bar";";
Depends on the handler. You can write your own handler to make sure it only happens as often as you like if you want to be absolutely sure about the behavior. There are 6 callbacks used to manage session variables. The one called "write" does not have to perform any real I/O and writing the session file could wait until the call to "close". It is an implementation detail that, as I said, depends on the handler.

Categories