Welcome!

Join our community of MMO enthusiasts and game developers! By registering, you'll gain access to discussions on the latest developments in MMO server files and collaborate with like-minded individuals. Join us today and unlock the potential of MMO server development!

Join Today!

[PHP] Session handling

Ginger by design.
Loyal Member
Joined
Feb 15, 2007
Messages
2,340
Reaction score
653
Preamble

So we all know them, and we love them. Sessions are awesome. Why? They let us pretend our stateless protocol is stateful! Awesome, now we can write truly interactive programs.

But... in light of bringing a frenzy of security analysis on what we already knew was insecure -- passing session data in plaintext -- I've decided to re-evaluate my session code and to my horror I realized there are some HUGE security flaws in my code. What I deem "SSS" (same-site scripting) exploits and session hijacking capabilities galore. I decided to fix it and to write a little guide here about what's bad and what most people are doing and why it's just plain wrong. Also, the example solution I present is resistant to plain-text session hijacking, especially when a landing page is HTTP and only the log-in form is SSL encrypted.

As a precursor, if you haven't heard of OWASP, but you're writing public-facing web applications, you may be a fool. You should check out their guidelines to developing securely, in particular, here's the one on sessions: . I'd advise you read this entire development guide as well ( ).

In that document, the outlined "things to do" is one of the key issues, "If applications use both HTTP and HTTPS, they MUST regenerate the identifier or use an additional session identifier for HTTPS communications." This is key, and not doing this makes HTTPS somewhat useless as session hijacking can be used to gain sensitive information.

I want to point out before I get started that I am NOT, again NOT implementing my own session management API here, and nor should you. PHP's available session management api is sufficient to properly secure applications providing sessions. You can emulate this behavior with your own code using cookies and files or database entries but YOU DON'T NEED TO. In fact, you can add these capabilities on top of the existing session management API quite easily, so don't re-invent the wheel here.

Introduction

So let's start where the tutorials leave off. A quick google for " " yields the following results:

The list goes on. Do you see a pattern here? Most of these pages aren't really tutorials at all, rather just an explanation of how to use a single PHP function. PHP F1 starts off decently explaining some of the information . But it quickly devolves into the same old thing. And we have the fun one at the end making an attempt to explain how to re-implement the session handling capabilities of PHP (yes, I do mean that HTML Goodies tutorial is BAD, do not follow its advice). The pattern is a very shallow explanation of PHP's session handling capabilities. The reason for this is because most basic PHP applications that we might make only really need a session_create and then a unset call later on clean up any session variables. That and sessions are a deceptively simple thing to deal with but are handled in a rather complex manner due to the stateless nature of HTTP and the fact that some pages may be HTTPS protected only increases this. In truth, to fully understand sessions in PHP in the most common manner (cookies), you really need to understand cookies in the HTTP protocol which, if you're so inclined, can be found . Cookies were added to implement statefulness to HTTP.

Let's start with the basics that nobody really explains very well. HTTP is at its most basic, most essential (insofar as you care about as a developer) is just a plaintext protocol with 2 commands: GET and POST. GET requests a specific file on the webserver, to which the webserver responds to by dumping the contents of the file (and mime information, blah blah blah) back to the client, or one of various error codes indicating some error. In the case that we have a scripting language tied into this processing stack, be it ASP, PERL, PHP, or something else, this GET usually causes your script to be executed and the output of it to be sent over instead of the file itself. The POST command sends data to the server along with the request, which your script then gets through the scripting environment.

Notice we don't talk about state management. That's because state doesn't exist in HTTP. State is loosely implemented with cookies. An example cookie header sent to the client over HTTP is:

Code:
HTTP/1.1 200 OK
Content-type: text/html
[b]Set-Cookie: name=value[/b]
 
(content of page)

(This example was taken from the wiki page on , take a read for more information)

Now notice the server tells the client to set a cookie with name 'name' to value 'value'. Now when the client sends a request to the server, it will send along a cookie:

Code:
GET /spec.html HTTP/1.1
Host: www.example.org
[b]Cookie: name=value[/b]
Accept: */*

This is highly over-simplified, as cookies have various other attributes such as an expiration and an option that determines whether the cookie should be sent over an HTTPS connection only or not, but we really only need to understand the basics.

So you see how you could say.. send a cookie to the client call it 'PHPSESSID' with some long unique string identifying a client, then create a file with that same name in some directory on the server, call it "C:\Sessions\" or "/var/sessions". For example, we send "PHPSESSID=30EF88B27A7AA09F" and I create files "C:\Sessions\30EF88B27A7AA09F.txt" or "/var/sessions/30EF88B27A7AA09F.txt".

Once this session is set up, every time a page request comes in, I read the cookies sent by the client, and when I find 'PHPSESSID' I look in "C:\Sessions" for that file. If it exists, I load it and pull the information out and populate some kind of an array. At the end of my page, I just need to save this array back to that file. And now you can see how I can store information about a specific client (up to the unique session ID) over the lifetime of their use of my application, called their "session."

If you're familiar with how HTTP works, you'll know the query string can also be used to send information from the client to the server. That's how GET variables work, for instance if you access a page like:

Code:
http://forum.ragezone.com/editpost.php?[b]do=updatepost&postid=6002552[/b]

Everything following the question mark, the part in bold, is the querystring of the URL entered. It's a list of ampersand separated variables (much like cookies except passed publicly and explicitly rather than privately and implicitly). You can also establish a session in this manner but it is less common and not recommended unless absolutely necessary (this is truly rare).

Sessions in PHP

Anyway, back to the topic. As you can see, the concept of establishing a session is not too difficult to understand. You also understand that at some point you want to delete their session file and if you have sensitive information there, you may want to erase it when they log-out.

This is the most basic session management you can implement. The function session_start in PHP explicitly creates a session for the client making the page request IF ONE DOES NOT ALREADY EXIST, then it fills the superglobal array $_SESSION with variables that exist in the person's session. By default it grabs the cookie 'PHPSESSID' and pulls the person's session information from a session file. You can visualize this as:

Code:
$sessid = $_COOKIE['PHPSESSID'];
$temp_session = file_get_contents(ini_get('session.save_path').$sessid);
session_decode($temp_session);

How PHP handles these cookies and session information can be modified by several php.ini settings. They are outlined , but the only ones I mention are below. These are by no means the most useful, so check out the rest sometime.

session.use_cookies - session.use_cookies specifies whether the module will use cookies to store the session id on the client side. Defaults to 1 (enabled).

session.use_only_cookies - session.use_only_cookies specifies whether the module will only use cookies to store the session id on the client side. Enabling this setting prevents attacks involved passing session ids in URLs. This setting was added in PHP 4.3.0. Defaults to 1 (enabled) since PHP 5.3.0.

session.cookie_lifetime - session.cookie_lifetime specifies the lifetime of the cookie in seconds which is sent to the browser. The value 0 means "until the browser is closed." Defaults to 0. See also session_get_cookie_params() and session_set_cookie_params().

There is a note here in the official documentation letting you know that this time is set relative to the server's time. That is because when setting a cookie's lifetime (even using the cookie API) the timeout is offset from the server's time. If this doesn't match the client's time, weird things can happen.

session.cookie_secure - session.cookie_secure specifies whether cookies should only be sent over secure connections. Defaults to off. This setting was added in PHP 4.0.4. See also session_get_cookie_params() and session_set_cookie_params().

session.cookie_httponly - Marks the cookie as accessible only through the HTTP protocol. This means that the cookie won't be accessible by scripting languages, such as JavaScript. This setting can effectively help to reduce identity theft through XSS attacks (although it is not supported by all browsers).

session.name - session.name specifies the name of the session which is used as cookie name. It should only contain alphanumeric characters. Defaults to PHPSESSID. See also session_name().

and finally

session.save_handler - session.save_handler defines the name of the handler which is used for storing and retrieving data associated with a session. Defaults to files. Note that individual extensions may register their own save_handlers; registered handlers can be obtained on a per-installation basis by referring to phpinfo(). See also session_set_save_handler().


So notice just looking at these configurable settings for session handling that we're referred to some dynamic session API that you may not have been aware of. Notably, session_get_cookie_params and session_set_cookie_params. We'll look into using these and a few other session management API a little later on to implement sessions properly and in accordance to some OWASP best practices.

Let's go back to what people usually do. So far we've seen some basic tutorials on "how to use sessions." This will probably get you with some pages like this:

Code:
<html>
<head>
<title>My First Session Page</title>
</head>
<body>
<?php
session_start();

if( isset($_SESSION['name']) ){
    $name = $_SESSION['name'];
    echo('<p>Hello '.$name.', welcome back!</p>');
}else{
    if( isset($_POST['name']) ){
        $name = $_SESSION['name'] = $_POST['name'];
        echo('<p>Hello '.$name.', I will remember you now!</p>');
    }else{
        echo('<form method="post" action=""><p>Please type your name: <input type="text" name="name" /><input type="submit" value="Go!" /></p></form>');
    }
}
?>
</body>
</html>

Now this doesn't do a lot, but it works, right? Now you may add some log-out capability with session_destroy. But have you read what that API does from the documentation? No? Let me get that for you:

session_destroy() destroys all of the data associated with the current session. It does not unset any of the global variables associated with the session, or unset the session cookie. To use the session variables again, session_start() has to be called.

In order to kill the session altogether, like to log the user out, the session id must also be unset. If a cookie is used to propagate the session id (default behavior), then the session cookie must be deleted. setcookie() may be used for that.

WHAT? Oh well that's fine right? We delete the data so we should be fine. No. We also want to regenerate the session key. But we wan't to do this safely.

This is where "SSS" exploits and long-lived session hijacks can creep in. It also lends itself to session collision which is a terribly annoying thing to deal with.

When I talk about "SSS" I really mean session collision. But I mean it in a way that can accidentally elevate privileges in another web application. So before discussing this, let's start with what session collision is.

Let's say you have developed some web application, let's call it A. This is hosted at . Now in this website, you create a session like above, and you assign two session variables username and access. Let's say these two are populated when a user logs in. We know if isset($_SESSION['username']) is true, that they've logged in. And we assign the access they have to $_SESSION['access'] so we can decide what they can do with that variable.

Now let's say you're working on another application on the same website. Maybe a bulletin board system for your users to get help. Call it B, and it's hosted at . Now when someone logs out of application A, you call session_destroy. Cool, it worked before. But wait, now you've logged them out of site B too, even if the two applications use a different authentication mechanism. Even if they use separate session variables to store the person's username and access level. This is session collision. When operations on one application's session impact another application's session. And you can easily see where "SSS" happens when site B has a separate authentication store but uses the same $_SESSION['username'] and $_SESSION['access'] variables. All I have to do is find an account on one that has elevated access on the other and create that account (if it doesn't exist) on the other. Then log-in, and navigate to the application I wanted to gain access to, and voila, I am privileged. This is essentially session-collision abused for privilege escalation.

You don't really run into this until you try to run multiple applications under the same domain. This is because cookies are domain-specific. If you're using the same webserver but different virtual servers based on domain, this isn't an issue. But the other security concerns are.

The next concern is session hijacking and information leakage and/or impersonation. Applications like Firesheep do what we could already do, but make it REALLY easy. When an HTTP request goes over a wireless connection, it's in plaintext. But on an unencrypted wireless network, everyone's packets are transmitted aloud via the radio in their computer (this is how wireless works). So other people can listen in to what you're doing. Since these pages are over HTTP, they're sent plaintext, so you can easily enough pull out the cookies being sent to the server, and in this case, specifically the PHPSESSID cookie. Now you just have to modify your browser's cookies with a little bit of javascript code to modify your own PHPSESSID to theirs and you can impersonate them.

Websites that use HTTPS for authentication only are not immune. Yes, the HTTPS protects the user's password from being sent in the clear, but that's not too important. Even if the website sits in HTTPS *USUALLY* but, for some things, perhaps AJAX requests (for efficiency) uses HTTP, if the website isn't secure, you can grab the user's PHPSESSID from these requests as well and through impersonation, gain access to these HTTPS pages and see all of the same information.

The real solution is what is outlined in the OWASP (Open Web Application Security Project) guidelines. Sessions should be regenerated often, depending on how sensitive the application is. Sessions should always, ALWAYS be regenerated when a user logs out of an application. And a new HTTPS ONLY session should be generated on the first HTTPS request and, optionally, tied to the HTTP session for user verification purposes. Further, it's best to also tie in more information about a user to verify the validity of the session identifier. For instance, the IP address of the user, the browser's user agent, and potentially the client's SSL certificate. For security purposes, it's also useful to log instances where invalid sessions are seen or brute-force attempts to use invalid sessions appear (this can be done with a little session-level counting and then a call to trigger_error). In addition, my own note, is that you should also use custom named sessions to segregate session handling for each web application, so that session collision cannot happen.

So on to the session API you probably didn't know about and that we can use to implement these requirements in a nice little session class. I'm not handling caching and sessions in this tutorial but I may in a later one. It's an important feature of PHP to understand and utilize to squeak out as much performance as possible from your applications.

- Get the session cookie parameters

This function will return an array of cookie parameters. The array returned includes the following keys:
  • "lifetime" - The lifetime of the cookie in seconds.
  • "path" - The path where information is stored.
  • "domain" - The domain of the cookie.
  • "secure" - The cookie should only be sent over secure connections.
  • "httponly" - The cookie can only be accessed through the HTTP protocol.

Of these, we really only care about lifetime and secure.

- Set the session cookie parameters.

- Get and/or set the current session name.

We can use this to implement application-specific session names to avoid collisions.

- Update the current session id with a newly generated one.

This one is just awesome. We'll use this to regenerate sessions on log-in or privilege change and periodically for high-risk applications.

- Get and/or set the current session id

We will use this sparingly so I want to make sure you're aware of it.

- Sets user-level session storage functions

I want to introduce this mainly to show how easy it would be to implement MySQL-based sessions, for instance. You can easily extend the Session class I will introduce to store partial information in a database, so this is just "cool" I suppose.

- Free all session variables

- Write session data and end session

I just want to mention this one as it may be pertinent to some people working with heavy AJAX'ed sites. Particularly, if two ajax requests go through simultaneously for requests that may take a while, since sessions lock the session file, the ajax requests may be serialized but the use of this API can in some cases correct this issue when all use of the session is through.

A PHP session wrapper class

The goal of this wrapper class is to encapsulate these API to perform proper session management automatically and consistently throughout any web applications we may write. This way the intricacies of session management can be abstracted away and we can comply with best practices without much thought.

-- TO BE CONTINUED --
 
Last edited:
ex visor
Loyal Member
Joined
May 17, 2007
Messages
2,741
Reaction score
937
*Claps*.
ATTN Children: Use sessions instead of cookies. kthnx
 
ex visor
Loyal Member
Joined
May 17, 2007
Messages
2,741
Reaction score
937
You must not know him. =3 It isn't leeched.
 
Joined
Jun 8, 2007
Messages
1,985
Reaction score
490
Cookies are good for some things... It's just that they can be whatever the user defines.. Much like an input field in a form).

Cookies are good for user-defined preferences, such as a theme switcher, remember me function, or a sweet tasty snack.

When security is important, use sessions.

Edit: It may also be important to note that cookies are saved in the browser until deleted by the user or their browser's/OS's preferred date. So cookies are great if you want your user's stored data to be remembered over long periods of time. Though this doesn't always work. (For example, common browser preferences, cCleaner and other programs delete cookies everytime the browser closes or the computer restarts. So use it, but don't rely on it...)
 
Last edited:
Junior Spellweaver
Joined
Apr 12, 2006
Messages
121
Reaction score
26
Is it me or did all the posters above got your tutorial wrong?

The point jMerliN is trying to make, is that sessions are NOT so secure as well. It has nothing to do with sessions vs cookies.

Point 1, privilege escalation through session collision.
Point 2, session hijacking through listening on an unprotected network; although, in my opinion, if you connect to an unprotected network then you should assume its consequences and I don't really see any way of protecting the session, except maybe by making a public/private key encryption like in SSH?

I am not going to re-explain those two, since I he developed and explained them well, and besides he knows more than me for sure. But, I will make a few images for you to understand the principles.

Basically, here is how a session works:

jMerliN - [PHP] Session handling - RaGEZONE Forums


A session id is stored on the client and is sent to the server, which is used to identify the session. The session variables are however, stored on the server.

Someone who is spying the network can find the session ID, and send that ID to the server.
jMerliN - [PHP] Session handling - RaGEZONE Forums


And the server recognizes him as admin.
jMerliN - [PHP] Session handling - RaGEZONE Forums


The other type of security flaw is a session collision.
It can be illustrated by these images (as we say in French, "un bon croquis vaut mieux qu'un long discours")

jMerliN - [PHP] Session handling - RaGEZONE Forums


jMerliN - [PHP] Session handling - RaGEZONE Forums


jMerliN - [PHP] Session handling - RaGEZONE Forums


jMerliN - [PHP] Session handling - RaGEZONE Forums


Leech?(Cous' it is long)(Not accusing)

Idiot.
 
Last edited:
Joined
Sep 10, 2006
Messages
2,817
Reaction score
1,417
Zend done pretty good job with sessions making it possible to basically use namespaces, haven't really read the whole thing you've written but I guess it's always relevant..

 
(oO (||||) (||||) Oo)
Loyal Member
Joined
Aug 6, 2009
Messages
2,132
Reaction score
429
Is it me or did all the posters above got your tutorial wrong?

The point jMerliN is trying to make, is that sessions are NOT so secure as well. It has nothing to do with sessions vs cookies.

Point 1, privilege escalation through session collision.
Point 2, session hijacking through listening on an unprotected network; although, in my opinion, if you connect to an unprotected network then you should assume its consequences and I don't really see any way of protecting the session, except maybe by making a public/private key encryption like in SSH?

I am not going to re-explain those two, since I he developed and explained them well, and besides he knows more than me for sure. But, I will make a few images for you to understand the principles.

Basically, here is how a session works:

jMerliN - [PHP] Session handling - RaGEZONE Forums


A session id is stored on the client and is sent to the server, which is used to identify the session. The session variables are however, stored on the server.

Someone who is spying the network can find the session ID, and send that ID to the server.
jMerliN - [PHP] Session handling - RaGEZONE Forums


And the server recognizes him as admin.
jMerliN - [PHP] Session handling - RaGEZONE Forums


The other type of security flaw is a session collision.
It can be illustrated by these images (as we say in French, "un bon croquis vaut mieux qu'un long discours")

jMerliN - [PHP] Session handling - RaGEZONE Forums


jMerliN - [PHP] Session handling - RaGEZONE Forums


jMerliN - [PHP] Session handling - RaGEZONE Forums


jMerliN - [PHP] Session handling - RaGEZONE Forums




Idiot.

Those are some good diagrams.
Question about security. Maybe possible store session_id and user_ip address in database. So when the page is called from a client only session with specific ip will have access. What are your thoughts?
 
Junior Spellweaver
Joined
Apr 12, 2006
Messages
121
Reaction score
26
Problem is, if someone is spying on the network, then he will have the same global IP address as the real client.
 
Back
Top