Tutorials

Sessions and cookies: Adding state to a stateless protocol

Views: 79004
Rating: 5/5
Votes: 6

Introduction

HTTP is a stateless protocol. This means that each request is handled independently of all the other requests and it means that a server or a script cannot remember if a user has been there before. However, knowing if a user has been there before is often required and therefore something known as cookies and sessions have been implemented in order to cope with that problem.

On page two we will look at cookies and sessions will be covered on page three. On page four we will talk a bit about security.

Cookies

Cookies are small text files placed on the client's computer. They are transmitted via headers sent forth between the client (in this case the browser) and the web server.

When the server tells the browser to set a cookie, the header might look something like this:

Set-cookie: has_been_here=yes

This tells the browser to save a cookie with the name has_been_here and the value of that cookie should be yes.

The full syntax of that header is:

Set-Cookie: <name>=<value>[; <name>=<value>]... [; expires=<date>][; domain=<domain_name>] [; path=<some_path>][; secure][; httponly]

We will talk about the other options later. When a browser has cookies for a specific page it will send them in a header like this:

Cookie: <name>=<value> [;<name>=<value>]...

In our above example it would then be:

Cookie: has_been_here=yes

So far so good... To make this a bit easier for us, PHP has some functions that will help us in using cookies. An important function is setcookie() [http://php.net/setcookie]. This function enables us to set cookies with the options explained above for the Set-Cookie HTTP response header.

Because the setcookie() function is sending headers, it is important that you call this function before any content has been output to the browser. Headers are always at the top of an HTTP response, so once you start outputting content you will not be able to send any more headers.

Setting cookies

The first two parameters of the setcookie() function ($name and $value) are obvious. They are setting the name and the value of the cookie, respectively. The other ones might not be so obvious. $expire is for how long the cookie should last. This is expressed with a Unix timestamp [http://en.wikipedia.org/wiki/Unix_timestamp]. You will have to set the date and time of when the cookie should expire. You will not be able to make a cookie that lasts "forever", but you can make the cookie last a long time (several years for instance). If you want to set a cookie that lasts 30 days, then you could get the timestamp like this:

$expire = time() + 60 * 60 * 24 * 30; // or 2592000
// ... or ...
$expire = strtotime('+30 days');

Both of those two things will get the current timestamp and add 30 days to it. You can then pass that value to the third parameter.

The fourth parameter, $path, is used to control which scripts that can access the cookie. This is relative to the document root. Say for instance you have http://example.com/forums/index.php and you want to set a cookie using that file. If you set the path to /forums, then the cookie will only be accessible to scripts within the /forums path. If you leave it blank then there is no restriction.

The fifth paramter, $domain, works much like $path does. However, this time the restriction is which domains that can access the cookie. If we say that you both have www.example.com and forums.example.com then setting $domain to forums.example.com will mean that the cookie is only accessible from the forums subdomain. You can also set it to .example.com and then all subdomains for example.com (e.g. www and forums) will be able to access it.

If you are using a free host where your domain name will be a subdomain of the host's domain (e.g. mysite.awesome-host.biz or something like that) then you might want to ensure that only your specific subdomain will be able to access your cookies so all the other users won't eat them.

$domain defaults to the hostname from which it was set. This means that example.com will not be able to get example2.com's cookies.

The two last parameters are a bit more advanced. If you set $secure to true then you are telling the browser to only send the cookie back to the server if the connection is encrypted using SSL (i.e. on a secure connection). $httponly is used to only transmit the cookie to the server, i.e. client-side scripting languages like Javascript will not be able to use them. This is a useful way of dealing with XSS [http://en.wikipedia.org/wiki/Cross-site_scripting].

Getting cookies

The cookies that are available will always be accessible using $_COOKIE. This variable holds an associative array of all cookies. To get the has_been_here cookie from earlier, you would access it using $_COOKIE['has_been_here']. $_COOKIE is a super-global so you will always be able to access it in PHP no matter where you are in the script.

When you set a cookie on a page it will not be immediately accessible through $_COOKIE, so don't try to use it.

An example of using cookies

Here is an example of a small script using cookies:

<?php
$visits = intval($_COOKIE['visits']);

setcookie('visits', $visits + 1, strtotime('+30 days'));

if ($visits == 0) {
	echo 'I have never seen you before, but I am glad you are here :)';
}
else {
	echo 'Welcome back! You have been here ', $visits, ' time(s) before';
}
?>

Try to upload the script to your web server and see what happens. Then try to refresh a couple of times and see the changes. We have now successfully added a state to a stateless protocol.

On the next page we will look at another way of doing this through the usage of sessions.

Sessions

Another popular method is using sessions. Sessions are very similar to cookies, but they are distinctive in a few important ways. While cookies are stored on the client machine, sessions are stored on the server.

The way sessions work are by generating a random ID for the user. This ID is stored in a cookie on the client machine. PHP will then fetch all session data related to that ID from wherever it is stored. By default it's stored on the harddisk, but it's possible to write your own save handler [http://php.net/manual/en/function.session-set-save-handler.php] so you can store it in e.g. a database. By default, the cookie will be called PHPSESSID and will expire when the browser is closed.

Seeing as you are using a cookie for identifying the user you'll need a way to set the same settings for the cookie as we did before. For this purpose you can use the session_set_cookie_params() [http://php.net/session_set_cookie_params] function. It has the following syntax:

session_set_cookie_params(int $lifetime[, string $path[, string $domain[, bool $secure[, bool $httponly]]]])

Using session data

Before you are able to use sessions you'll have to call session_start() [http://php.net/session_start]. This function will, like when setting cookies, have to be called before any output has been sent to the browser. Once this has been done, storing data in sessions and retrieving the values is quite easy:

$_SESSION['has_been_here'] = true;

Like $_COOKIE, $_SESSION is a super-global as well.

An example of using sessions

This is how the script we made using cookies on the previous page will look if we decide to use sessions instead:

<?php
session_start();

$visits = $_SESSION['visits']++;

if ($visits == 0) {
	echo 'I have never seen you before, but I am glad you are here :)';
}
else {
	echo 'Welcome back! You have been here ', $visits, ' time(s) before';
}
?>
Sessions without cookies

Sessions do not always have to use cookies. Whatever value you choose for the session name (PHPSESSID by default) just have to be present somewhere. It could for instance be through the URL like this: http://example.com/index.php?PHPSESSID=jfJk87Hfja87Hajhsd97Hash or it could be through a form field (either via POST or GET). The manual has more information about this, but I would recommend you just use the cookies.

On the next page we will talk a bit about security...

Security issues

Because the cookies are transferred unencrypted (unless you are using SSL) and because they will be stored in plaintext on the client computer, it is not advised that you store any sensitive information in the cookies. Such information could, for instance, be the username and/or password. For purposes such as authentication it would be best to store a unique identifier in a cookie and store the rest of the information on the server. In that way it never leaves the server and therefore it is stored in a safe manner (providing your server is secure). Seeing as sessions do this, they would be a better choice for that purpose.

An instance where you would not want to use sessions instead of cookies would be if you need Javascript or another client-side scripting language to access the information. Seeing as session data is stored on the server, there is no way whatsoever it can be access from the client machine. Again, this information should not be sensitive.

A problem related to this is a security issue known as XSS (Cross Site Scripting). This vulnerability exists if users are able to post information (via a form or the URL for instance) and that information is redisplayed on a page without being properly escaped. In that way a malicious user will be able to execute any arbitrary Javascript code on the client machine without the user or you knowing it. The reason why this is a problem regarding cookie and session security is that cookies will be default be able to be viewed by Javascript. There are a number of different ways you could deal with this. The first and most obvious one is to properly escape all output coming from a user. The function htmlentities() [http://php.net/htmlentities] can take care of that. Another way is by setting the httponly parameter for your cookies. In that way the browser will not give Javascript access to the information that cookie holds. If you are using sessions, then you can call session_regenerate_id() [http://php.net/session_regenerate_id] on every request. By doing that the user will get a new session ID each time and it means that if a malicious user ever gets a session id, chances are it will be invalid by the time he gets to try it because it changes often. You can use any combination of these three things (although you should always do the first). Stealing the session IDs and attempting to use them is known as session fixation. There are plenty of resources available regarding both XSS and session fixation available if you want further information regarding those two subjects.

Conclusion

That's all I have to say about cookies and sessions right now. With this new information you will be able to remember things about your users.