Do you need to encrypt session data?
Encrypting your session data in PHP is a bit like locking your car and leaving your keys on the roof. Consider the threat scenarios that apply:
- An attacker gains some remote access to the server and attempts to read the session data files.
- An attacker steals the server's entire hard disk from the datacenter. This could be through physical theft or through accessing a virtual hard disk file in a virtualised environment.
In the first scenario, the attacker needs to gain access to the server, then either privilege escalate to root or impersonate the user that owns the session data directory (e.g. www-data
for Apache, nobody
for nginx). These directories have access controls applied (usually 0600
permissions bits) that prevent users other than the owner from accessing the data.
If an attacker already has access to the server and can impersonate the user that the web server daemon is running under, then they can by definition access all of the data that the web server process can access, meaning that they by definition would have access to any encryption keys that the web server or the code running under it might be using (with the exception of a HSM, although that it isn't a reasonable assumption that one would be in use here).
This means that if the attacker can read the session data files, they can also read the encryption keys, which means they can decrypt the session data. The encryption ends up being obfuscation at best.
In the second scenario, the same issue applies: if the key is on the hard disk along with the session data, an attacker can simply read the keys and decrypt the session data. The way to resolve this problem is by encrypting the session data in such a way that the keys are not stored on disk, and the correct approach is to use full-disk encryption (FDE) and manually enter the password at boot using out-of-band management (e.g. iLO, DRAC, remote KVM, etc.) so that the entire hard disk is encrypted in such a way that does not allow an attacker to gain access to any data on the disk if it is stolen.
However, even in this scenario, it may turn out that FDE isn't that much of a security gain in terms of protecting session data. The reason is that, on most platforms and configurations, session data is stored in /tmp
, which is backed by a tmpfs volume. The whole point of tmpfs is that it is volatile (i.e. the data is lost on reboot) and in most cases the contents are stored in RAM. Data that is not touched for quite some time may be swapped out to disk, which is where FDE does provide a benefit towards securing session data, but the likelihood is that any old sessions will have expired before this happens. That being said, I still recommend the use of FDE on all systems as part of good security practice, because it protects against other types of data theft (e.g. infrastructure credentials, SSL certificate private keys, etc.)
The only situation in which encrypting session data in PHP makes some sense is if you have session data being stored in a database or key-value store. In this scenario you should expect that the session data will likely be nonvolatile (i.e. stored on disk) and that there might be a situation in which an attacker compromises the database (e.g. getting into an overlooked phpMyAdmin instance) but not the filesystem. In such a case the key would be on the filesystem and inaccessible to the attacker. This is, however, an exceedingly rare case.
One other argument I hear a lot is that certain data must be encrypted for compliance reasons, e.g. PAN data under PCI-DSS. The solutions here depend upon the exact system architecture, but generally speaking you fix this by either not storing the data in the session data at all, by encrypting/decrypting the data using an external broker/HSM so that the keys are isolated, or simply with FDE if you only require offline encryption of data at rest.
While I don't generally recommend it for the reasons that are discussed above, if you're set on encrypting session data in PHP for whatever reason, it's actually quite possible to do it within PHP without modifying every use of $_SESSION
. You can use the SessionHandler class to override normal handling of sessions. The documentation provides a simple explanation of how you can encrypt session data.
However, you'll need a way of creating and storing the key across the session. Obviously this can't be done through the session manager itself, otherwise you'd be distributing the key with the ciphertext. My suggestion would to be use an in-memory cache, such as APC or memcache, where the key name in the cache is the session ID.
If you are using a reasonable web framework (one that has a halfway decent design), you do not need to encrypt session data. That really ought to be the responsibility of the framework.
However, if you are using PHP, you are not using a reasonable web framework. PHP is a problem child for security, in so many ways. One of those ways is that, by default, it stores session data in /tmp
. On some shared hosting services, /tmp
may be shared across users. So, PHP is storing session data in a location where others may be able to view it, without authorization.
That is a bad design flaw -- but hey, PHP is full of bad design flaws, that's what life with PHP is like.
So, if you are using PHP on a shared hosting service (where /tmp
is accessible to others), yes, you need to encrypt session data. One way to do that is to use the session_set_save_handler()
hook to encrypt the session data. Make sure you use strong encryption (use authenticated encryption, avoid common crypto pitfalls) with good key management (for heaven's sake, don't store the crypto key in /tmp
or anywhere else that other customers of your shared hosting service can see it).
Or, host your PHP application on a dedicated machine (e.g., a dedicated virtual machine; a VPS; a dedicated physical machine), and don't let anyone you don't trust log into that machine. Or, if you were somehow confident that you're not storing anything sensitive in the session store, you don't need to encrypt the session data (but this is fragile under maintenance, as it would be easy for someone else to add a feature in the future that happens to add some sensitive information to the session store without realizing the consequences for security).
But really, a better answer is: use a serious, well-designed web framework. Friends don't let friends use bare PHP for security-critical problems. See, e.g., Jeff Atwood's blog post on this topic.
It doesn't make a lot of sense if the key is stored serverside - but the key might come from the browser / user. It's more dificult to access the key in this scenario (provided it's over SSL) even with some access to the server / source code.
Session data may be stored on the filesystem / in a database - and hence persist in backups. Also, on a shared system, depending how it is configured, this provides security-in-depth for session data - where one user account maps to one site, it's easy to restrict ssh / ftp access, but restricting PHPs access to specific directory trees is a bit more complex (there are some ways to get around open_base_dir); for low end hosting, it just doesn't make financial sense to run each webserver as a seperate uid / seperate FPM groups for each site.
While nobody in their right mind should be storing credit card details on such a system - what if you just want a simple front end for occassionally invoking other services - e.g. polling availability / performance of another server over ssh, sending an SMS, or accessing a mailbox? In these cases it's quite possible that you might temporarily keep a secondary authentication token / encryption key / remote session identifier inside the session.
While it's trivial to restrict write access to PHP src files to a small set of users other than the webserver uid, the webserver uid must be able to write session data. Once a user is authenticated, authorization is typically based on the authenticated user id stored in the session - hence this may be part of a solution to protect against privilege escalation (needs other components not mentioned in post).
It will help security in some edge cases depending on how the key is managed.