A BASIC AND COMPLETE INTRODUCTION TO PHP SESSIONS
You never used PHP Sessions and you want to get started? Or maybe you already use them but you want a better understanding of how they work?
Then this guide is for you.
In this post you will find a simple but complete introduction to Sessions with some examples on how to use them, a more in depth analysis of the most important functions and some facts about security that you should be aware of.
WHAT ARE SESSIONS
AND WHAT CAN YOU DO WITH THEM?
The main purpose of PHP Sessions is to establish a stateful link between a website and the remote clients, with the ability to preserve informations across subsequent client connections.
By using Sessions you can associate a set of variables with the client accessing your PHP script, and automatically restore the same variables the next time the same client will connect again.
Sessions can be used, for example, in e-commerce sites to remember which items each client put in its cart, or in password protected websites to know if a client trying to access the site has already been authenticated.
In these example cases, the first time a client accesses the website a new Session is started. The website’s PHP script can then save some variables along with the Session, for example which items have been put in the cart or a boolean value telling whether valid authentication credentials have been provided by the client.
The next time the same client will access the website, its Session will be restored along with all the previously saved variables. This means that the website’s PHP scripts will know which items have been put in the cart or if the client is authorized to access the protected site.
Now let’s see how to use Sessions in practice.
Using Sessions is very simple.
The first thing you need to do in your PHP script is to start a Session with the session_start() function. Every single remote client has its own Session, and session_start() takes care of checking if a Session has already been started for the currently connected client; in that case its Session is restored, otherwise a new one is created.
After starting a Session, the $_SESSION superglobal array becomes available. This is where you can store all the Session’s related variables, as all variables inside this array will be saved and retrieved the next time the same client will connect.
This is basically all you need to know to start using Sessions, at least as a start.
Now let’s see a basic example.
Suppose you would like to know how many times the currently connected client has visited your page. For doing that you first you need to identify the client, then you need to keep track of how many times that very client has connected to your page.
Remote client identification is automatically done simply by starting a Session with session_start().
That will also create the $_SESSION superglobal array, the one place where all the Session’s data can be stored and retrieved. Each different remote client has its own $_SESSION array, which is properly created and populated every time the same client accesses your page.
Keeping track of each client’s page accesses is just a matter of starting a Session and using a numeric variable inside the $_SESSION array. For example, you could use $_SESSION[‘visits’] for that purpose.
Using a normal variable like $visits wouldn’t work, because it would be destroyed at the end of the script execution and never retrieved.
The first time a client accesses the page the $_SESSION array will be empty and the ‘visits’ key won’t exists. If this is the case you need to initialize it to 0, otherwise you just need to increment it by 1.
Here is the code:
/* Start the Session */
/* Check whether we already set 'visits' for this remote client */
if (!array_key_exists('visits', $_SESSION))
$_SESSION['visits'] = 0;
echo 'You visited this page ' . $_SESSION['visits']++ . ' times.';
Easy, isn’t it?
Let’s see another basic example. Suppose you have a private page, and that you want to show its content to authorized clients only. The remote client must provide proper user and password to view the page’s content.
If the remote client isn’t authenticated then the PHP script will show a login form, otherwise it will show the private content.
One way to do that would be to always check for user and password, but that would force the client to provide them in the request string every time it accesses the page.
Instead, you want the client to provide its credentials just once, and then be able to access the private content directly.
This can be done easily with Sessions (note: this is not meant to be the proper way to authenticate users, but just an example of how Sessions work!)
Here is the PHP code:
/* Start the Session */
/* Is the client authenticated? */
$auth = FALSE;
/* Check the Session array to see if this client is already authenticated */
if (array_key_exists('auth', $_SESSION))
$auth = TRUE;
/* Check the request string for user and password */
$user = '';
$passwd = '';
if (array_key_exists('user', $_REQUEST))
$user = $_REQUEST['user'];
if (array_key_exists('passwd', $_REQUEST))
$passwd = $_REQUEST['passwd'];
/* Example authentication */
if (($user == 'user') && ($passwd == 'passwd'))
$auth = TRUE;
/* Save the authorized state in the Session array */
$_SESSION['auth'] = TRUE;
echo 'Here is your private content.';
/* Show the login form */
<input type="text" name="user">
<input type="password" name="passwd">
<input type="submit" value="Log-in">
In this example, the boolean $auth variable is set to TRUE if the remote client is authorized to access the private content. That happens in two cases: when the client actually sends the user and password request strings, and when the Session variable $_SESSION[‘auth’] is set.
When the remote client authenticates itself with user and password, the script sets the Session’s auth variable $_SESSION[‘auth’]. This way, when the same client will access the page again it won’t have to send user and password again, but its Session state will “remember” that it has already been authorized.
Of course, unsetting $_SESSION[‘auth’] will force the remote client to send its credentials again.
Now you should have an idea of how Sessions work in practice. Let’s move on to the next section.
A FEW IMPORTANT FACTS ABOUT SESSIONS
There are a few important facts about Sessions that you should know before using them:
- $_SESSION variable types
You can store all variable types inside the $_SESSION global array as long as they are serializable. Not serializable types include local and remote file handles, database connection resources, sockets and so on. These variable types cannot be stored and retrieved across subsequent client accesses.
- $_SESSION scope
$_SESSION is a superglobal array: this means that it’s accessible from anywhere inside your PHP script (including from inside functions and classes) without the need to declare it global.
- Session locking
PHP keeps Sessions’ data inside files. PHP scripts needs to acquire an exclusive lock on the data file relative to the current Session, and other scripts (or other instances of the same script) cannot acquire the same lock before it’s released.
In other words, scripts executed by the same remote client cannot use Sessions at the same time but must wait for the previous script to terminate.
This can cause delays in some cases, for example when a web page sends many simultaneous AJAX connections. In that case, every AJAX backend (if it uses Sessions) needs to wait for the previous one to terminate before being able to create its own Session.
In the next chapter you will see how this situation can be mitigated.
- Sessions cookie lifetime
How long does a Session lasts? The Session lifetime is the maximum time interval from when the Session is created for the first time until it expires. When a Session expires, all the variables inside $_SESSION are destroyed and lost, and the cookie used for client identification is no longer valid. A new Session must therefore be created.
A new remote client who hasn’t a Session yet and a remote client whose Session is expired can be treated the same as long as the PHP code is concerned, as in both cases no Session exists for that client and a new one must be created.
By default, a Session will only last until the remote client will close its browser. This may or may not the best setting for your application, depending on how you want to use Sessions. In the next chapter you will see how you can change this value.
SESSIONS FUNCTIONS AND SETTINGS
There are many Sessions related functions that can be used to customize Sessions’ workflow, but just few of them are commonly used in real scenarios.
Many of these functions solve very specific needs (like session_reset() that re-initialize the $_SESSION array with its original values), and it doesn’t make sense to learn all of them right from the start. You can just go and check them when you will need to.
Some other functions are security related, for example session_regenerate_id(), but we will talk about security in the last chapter.
The most useful Sessions related functions are session_start(), session_name(), session_set_cookie_params() and session_write_close(). These are the ones that you will probably have the chance to use.
session_start() starts the remote client’s Session and makes the $_SESSION global array available, as already explained. If no Session already exists for the currently connected client then a new one will be created, otherwise the existing one will be restored.
This function takes as optional argument an array of Sessions options that override the default ones. We will see shortly which of these options are worth considering.
On top of that, the options array passed to session_start() can also have an additional key: ‘read_and_close’. If this option is set to TRUE, then the Session data (i.e., the $_SESSION array) will be read-only.
Remember how multiple instances of the same Session cannot be executed at the same time? This option can help with that. If you just need to read Session data without making any changes, this option will limit the Session lock time to the initial read phase only.
If your web application has many concurrent HTTP requests (usually AJAX calls), then this option may be useful for maximising the response time.
It’s important to keep in mind that session_start() must be called before any output is sent to the browser. This is a common source of errors, for example if the PHP file has an empty line at the beginning.
session_name() changes the current Session’s name and the name of the Session Cookie sent to the remote browser. This function must be called before session_start(). The default Sessions’ name is “PHPSESSID”.
You may want to change the name of your own Sessions to customize your application’s appearance and the cookie the clients’ browsers will receive.
This is an example on how to set a custom name for your Session:
/* Set your Session name */
/* Start the Session */
/* Do other things... */
Remember that when closing a Session (you will see later in this post how to do it) you need to clear the proper cookie. If you use a custom Session name, then you need to use that name for clearing the Session Cookie as well.
This function sets some options for the current Session. It can set up to 5 options:
The lifetime, in seconds, of the Session. This is an important option: it tells how long this Session will remain active. So, if you set this value to 3600, this Session will expire after one hour. This means that if the same remote client will connect more than one hour later, a new Session will be created and the $_SESSION array will be empty. This option is useful, for example, for setting a “login timeout”.
The lifetime is relative to the first time the session is created, so even if the remote client accesses the page again the Session timeout won’t change (for it to change, you either need to close and start the session again or increment the lifetime value).
Setting this option to 0 (zero) has a special meaning: the Session will be valid only until the client closes the browser. This is also the default value.
The site path on where the Session is enabled. For example, if your web application’s URL is www.mysite.com/myapp, setting this parameter to the default (“/”) will make the Session valid throught the www.mysite.com domain. If you set $path to “myapp”, then the Session will be valid only for pages within the www.mysite.com/myapp path.
This option is similar to the previous one, but it refers to the domain itself. Usually you want to leave this parameter to the default value (the domain name of the server), but you can change it to restrict the domain.
If this parameter is set to TRUE, then the Session will work over secure connections only (i.e., HTTPS). This is a good idea if your applications deals with sensitive or critical data, as Session Cookies sent over unencrypted connections can be read and used for Session hijacking (see the last chapter for more details).
Setting this parameter to TRUE will send a request to the remote browser to use the Session Cookie for HTTP(S) requests only. This may give you a bit of added security.
This function explicity closes the Session, which is usually done automatically when the script terminates. This is useful for the locking problem we have already talked about: this function let the script “release” the Session lock as soon as possibile, minimizing the time other scripts have to wait for acquiring the lock.
You should call his function when you don’t need to work on Session data anymore and your script still has some execution time left.
You can also customize the global Sessions’ behaviour by changing some of the default settings, either globally in the PHP configuration file (editing the php.ini file) or at runtime.
The full list of options is quite long, but here are the ones you should care about:
This option sets the path where the actual Sessions’ data is stored on the file system. It’s ok to just leave the default value (“/tmp” on Linux and *nix systems), but you should be aware that this may not be the best option for security. We’ll talk about this in the last chapter about security.
This option sets the same parameter as the session_name() function does. You can change this option globally in order to use your custom Session name by default.
This option sets the same lifetime parameter you can set with session_set_cookie_params(). By default, Sessions will remain active only until the remote client closes the browser; while this can be too limiting in many cases, it’s also true that different web applications needs very different Session times. For example, a home banking app may stick with the default, while a less critical service may set a week long value as well. My suggestion is to keep the default as it is and to change it when needed at runtime.
- session.cookie_path and session.cookie_domain
We already saw this options before in the session_start() options. If you always use different values from the default ones, it can be a good idea to modify them in the global configuration.
HOW TO DESTROY A SESSION
A Session will automatically expire after the timeout, but you may want to explicitly close it as well (for example when the client “logs out”).
Closing (or destroying) a Session means deleting all the variables inside $_SESSION, deleting the server-side data (the file where PHP stores the Session’s data) and deleting the Session Cookie from the client’s web browser.
Here is how to do it:
/* First start the Session */
/* Unset all $_SESSION variables */
$_SESSION = array();
/* Clear the Session Cookie */
$cookie_par = session_get_cookie_params();
setcookie(session_name(), '', time() - 86400, $cookie_par['path'], $cookie_par['domain'], $cookie_par['secure'], $cookie_par['httponly']);
/* Destroy the session data */
Notice that you need to start the Session before you can destroy it.
Clearing $_SESSIONS (at line 7) deletes all the Session variables, and the session_destroy() function (at line 14) takes care of the server side Session data.
(Note that setting the cookie timeout in the past makes the remote browser delete the cookie).
Sessions are often used for authentication purposes, for storing and providing private and sensitive data and for other critical applications, so it’s important to understand how secure they really are and how to maximise security.
As far as security is concerned, Sessions have two major weak points: cookie hijacking risk and storage leaking.
Let’s see what kind of risk they are and what you can to do mitigate them.
Cookie Hijacking refers to the act of stealing a Session Cookie and using it for maliciously accessing a website.
This can be done in a few different ways: sniffing the network (if HTTPS is not used), gaining remote or physical access to the client’s computer, or exploiting a browser security flaw.
Cookie Hijacking can be prevented by using HTTPS (so that all HTTP data, including cookies, are encrypted) and securing the remote computers.
While the use of HTTPS can be enforced by the web server itself, you cannot do very much about remote client’s computers security.
PHP has some ways to mitigate this problems, like dynamically changing the session ID and keeping the lifetime short enough.
Some specific configuration options can help increase Sessions security. Some of them are already set by default, but others are not. I suggest you to enable session.use_strict_mode and, if you are going to use HTTPS only, session.cookie_secure too. This two options (that are not enabled by default) will easily increase Session security without any relevant side effect.
PHP has also more sophisticated tools to go even further, for example dynamically changing the Session ID at every iteration (with session_regenerate_id()), however pushing security too far can create other problems. For example, dynamically changing the Session ID can cause problems with AJAX as the Session ID can change between multiple asynchronous HTTP requests cutting some of them out.
Even with all the security features PHP offers, Sessions can be secured only up to some level and it’s impossible to achieve a very high security level using Sessions only.
If your application needs a high level of security then you have to use only a subset of Sessions functionalities, for example keeping Sessions open only until the remote client closes its browser. Some very critical data should even be kept completely outside the scope of Sessions and should explicitly require user and password to be retrieved.
Have you even noticed how some services (like Google) ask again for your password when you try to access some sensitive data, even if you are already logged in? This happens because the cookie your browser uses to log-in is not secure enough for that data, and a proper authentication (with username and password) is required.
The second major security risk is related to storage. By default, PHP saves its Sessions data inside /tmp (on *nix systems like Linux), which is a directory every system user can read. This is indeed not very secure, and you should use a directory readable by the web server only.
You should however keep in mind that the Sessions data can only be as secure as the server itself, so you should avoid keeping sensitive data inside Sessions’ variables in the first place.
This is also one of the reasons I prefer not to use Sessions for user authentication. In my user authentication tutorial I store the cookies on the database (encrypting them in the process), so that there is no way to stole a login cookie from the server (or even from the SQL server, as only the hash is stored).
For maximise security, Sessions’ data should be stored inside a directory readable by the web server only and outside the web directory root. This is critical, otherwise the Sessions’ files could be read from the Internet.
There could be other ways to even improve storage security, like using encrypted file systems or SANs (storage area networks), but my opinion is that if you are dealing with such critical applications then it would be better not to rely on Sessions in the first place than trying to secure them beyond reasonably possible.
This guide ends here, I hope it can be helpful to you. If you have any questions feel free to ask in the comments below, and if you liked this guide please take a second to share it!