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.