AppSec Blog

Session Attacks and PHP

This blog is of course inspired by Jason's ASP .Net blog. I figured as the PHP guy in the group, I may as well cover what he did for .Net from the PHP side.

PHP's default session mechanism is rather simple and effective. The php.ini file configures how sessions work. Many of the parameters can be overridden within your PHP code, or .htaccess files can be used to create more fine grained configurations for particular directories. The session module is part of PHP by default, but can be disabled at compile time. By default, the session data is saved in files. The directory the session data is stored in is again configured in php.ini and defaults to /tmp (not the best choice, but more about that in a later blog).

Much of the session module can be adjusted, or custom code can be used to store session data in a database.

Like Jason noted for .Net, sessions and authentication are two different things in PHP as well. It is up to the developer to use sessions to store the user's identifier.

Let's follow Jason's outline, and talk about session fixation first!

Session Fixation

PHP provides a number of defenses to prevent session fixation. First of all, the lifetime of a session can be limited in php.ini, or by simply using runtime configuration directives. The one "gotcha" here is that some developers mistake the cookie lifetime for the session lifetime. The session lifetime is the part that counts. It is adjusted via gc_maxlifetime parameters. The "gc" (Garbage Collection) parameters are a bit hard to grasp for someone new to PHP. First of all, it is important to understand that nothing happens in PHP unless a page is rendered. Session data will survive indefinetly if the server is idle. Whenever a page is displayed and a session is initiated, the garbage collection functions run and clean up old session data. There are a total of three parameters that determine how this is done:

gc_probability and gc_divisor: How likely is it that the garbage collection is performed. The probability is calculated as gc_probability/gc_divisor. The default is 1/100, which may be a bit a lot probablility if you only have 100 page views per hour. But if you have 100 page views per second, this is perfectly fine.

gc_maxlifetime: This parameter defaults to 1440 seconds (15 minutes). After 15 minutes of inactivity, the session is considered for garbage collection.

So in short: Using default parameters, the session is deleted within 100 page views after the 15 minute time out expired, assuming that each page view initiates a session.

What does this mean for session fixation and reusing session data: The time window is about 15 minutes by default, which is appropriate for most applications.

PHP does allow for a "referrer check". Sessions will only be considered if the referrer contains the string defined using the "referer_check" configuration parameter. By default, this parameter is empty. This is a very powerful way to block many session fixation attacks.

It is also rather simple to change the session ID in PHP. session_regenerate_id will create the new session and move the data. Take care to set the optional parameter to "true". Otherwise, the old session will not be deleted. For the paranoid, it is as easy as adding "session_regenerate_id(true)" to your header file. (and wait for entropy starvation to set in ? ).

Now what about the attacker obtaining a valid session id? PHP allows for sessions to be delivered via the URL, or cookies. URL based sessions are disabled by default via the "use_only_cookies" parameter. The cookie itself is configured via php.ini.

Cookies can simply be configured as http_only and secure via php.ini. No need for extra code on this one.

Another item not discussed (yet?) by Jason is the session ID generation. PHP's session ID generation is reasonably secure by default. It is possible to define the source of the entropy used to create sessions (/dev/random or /dev/urandom), how many bytes of entropy are used and which hash function is used. Plenty of ways to mix it up!

There is a lot more to talk about when it comes to PHP sessions. Let's see what Jason is up to next! Also note that the facts above are valid for later versions of PHP (5 and later). Maybe I should also write about suhosin one of these days, an excelent PHP hardening module.


Posted June 24, 2009 at 11:37 PM | Permalink | Reply


I know that in Kohana PHP, the session handling has a configuration argument about regenerating the sessions id on n# of loads. It's defaulted to 3. So every 3rd page load a browser does, a new session id is generated.

Posted June 25, 2009 at 4:14 AM | Permalink | Reply


I don't see the relationship between your article and Jason's besides telling on how the sessions work in the different languages. The article written by Jason is about the problems with ASP, as in how it handles the sessions, sets them, and what needs to be fixed. It has lots of inherent problems with how it handles such things and he points them out. PHP has done a rather good job, as your article points out here, in handling sessions. However, I did enjoy the refresher course article and am looking forward to why you believe /tmp is a bad place to store session data and what alternate location you suggest and why.

Posted June 25, 2009 at 4:10 PM | Permalink | Reply


/tmp is bad on shared servers since all users will have access to it. All they have to do is "ls /tmp" to get a list of active sessions (sess_* by default), then start manually testing each one.
If you are not on a shared server, this isn't a major issue.

Posted June 28, 2009 at 9:56 PM | Permalink | Reply


So instead your web server should have access to a file with higher level permissions?

Posted June 28, 2009 at 10:01 PM | Permalink | Reply


I don't see how placing it in a different location is better. It thought the beauty of placing it in /tmp was that it could be accessed by anyone. I don't see how allowing whatever user/group (nobody?) access to a file with higher level permissions makes it that much more secure?

Posted June 29, 2009 at 12:46 PM | Permalink | Reply


First of all, the web server should *NOT* be running as nobody. It should run as a dedicated user. The goal of placing the sessions in a dedicated directory is to allow *ONLY* the web server to access them, and nobody else.

Posted June 29, 2009 at 3:26 PM | Permalink | Reply


TurboBorland: Think of the following situation ''" 5 clients on one shared server, using /tmp. All you have to do is test the /tmp/sess_* against each of those 5 clients.
But lets not stop there! Lets say you have a dedicated server used by only trusted people, but you have an error in one of your web applications that has a file download tool that doesn't properly validate the download path (oops!). On Unix, running PHP's "getfilecontents" of a directory will result in the filelist for the directory (oops!). And lets say someone uncovers this and browses over to /tmp, reads your session names, and tries them out (ouch!)!
Good validation is very, very important (as clearly evidenced here), but so is keeping malicious attackers away from your session list. A "simple" but dangerous mistake can cause your entire application security to suddenly explode.

Post a Comment


* Indicates a required field.