11

I came across a session management class in PHP which encrypts session data in the session storage folder (i.e., /tmp) and can be decrypted later in your script using a key. I was wondering if it's really needed? If you already doing some session hijacking prevention like this (simplified) example:

session_start();

if (isset($_SESSION['fingerprint']))
    if ($_SESSION['fingerprint'] != md5($_SERVER['HTTP_USER_AGENT'].'SECRETSALT'))          
        exit; // prompt for password
else
    $_SESSION['fingerprint'] = md5($_SERVER['HTTP_USER_AGENT'].'SECRETSALT');

Do you still need to encrypt your session data? Or is encryption only needed if you are storing sensitive info via sessions (e.g., personal info, cc number, credentials) ?

Moreover, if you are validating whether a user is logged-in or not in a simple way like this:

// login.php

if ($_POST['password'] == $db_password)
{
    $_SESSION['logged_in'] = true;
    redirect_to_protected_area();
}
else
    // show login again

// protected_area.php

if (!isset($_SESSION['logged_in']) OR !$_SESSION['logged_in'])
    exit; // prompt for password
else
    // show protected area

What damage can be done if session data is unencrypted and a hacker saw the data in plain sight (i.e., the md5 hash of the fingerprint and the logged_in = true). Can he actually log himself in or he must first "crack" the md5?

Note: md5 was used to simplify example, a much better hashing algo is used in real life.

IMB
  • 2,888
  • 6
  • 28
  • 42
  • @curiousguy What do you mean? If you mean `/tmp`then `/tmp` is PHP's default session folder in a shared hosting environment. – IMB Aug 19 '12 at 18:08
  • Sorry I wasn't clear. Is `/tmp` written on disk storage? Is it a `tmpfs` filesystem? – curiousguy Aug 19 '12 at 18:10
  • @curiousguy I am not sure what `tmpfs` is but `/tmp` is usually just a regular folder in windows or linux. More info about it here: http://www.php.net/manual/en/session.configuration.php#ini.session.save-path – IMB Aug 19 '12 at 19:10
  • 2
    I think IMB is talking about encrypting session data in `$_SESSION` so that attacks that read files in `/tmp` can't divulge user info. – Polynomial Aug 19 '12 at 19:55
  • @Polynomial That's exactly what I mean. Do you think it's necessary to encrypt it if you're not storing sensitive info in the session data? – IMB Aug 19 '12 at 20:23

4 Answers4

11

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:

  1. An attacker gains some remote access to the server and attempts to read the session data files.
  2. 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.

Polynomial
  • 132,208
  • 43
  • 298
  • 379
  • Thanks, BTW here's an example of the script I'm talking about http://www.zimuel.it/en/encrypt-php-session-data/ it's unclear what's his purpose. Maybe he's storing sensitive info in sessions? He stores the key in the user cookie and encrypted data remains in `/tmp` (or the designated session path). Regardless, my question is more on why is it necessary to encrypt it? For example, if I don't encrypt data and if `/tmp` was compromised, an attacker really can't do anything with it right as long as there is session hijacking prevention? – IMB Aug 19 '12 at 20:48
  • They might still extract personal / sensitive information in `$_SESSION`, and session hijacking prevention isn't 100% effective. For example, if you lock sessions to IP addresses, an attacker could still steal the session from the same network as the user. It's a corner case, but if you're using the `SessionHandler` class there's almost no added complexity. – Polynomial Aug 19 '12 at 20:55
  • Ok you mention `personal / sensitive information`, so if I'm not storing this kind of info (i.e., I only store `loggedin = true`), then there's no need for encryption? – IMB Aug 19 '12 at 20:59
  • Sure, it's completely unnecessary in that case. – Polynomial Aug 19 '12 at 21:00
  • 1
    +1 It always bugs me when people want to add "encryption" to a system just because it sounds cool or it meets some compliance requirement. Encryption is -- at best -- only as secure as the key management. – Mark E. Haase Aug 21 '12 at 14:44
  • What if the key is stored on the same system in a safer place then for instance the `/tmp/` folder where the `$_SESSION` data might end up? If the for instance sensitive data was stored in the `$_SESSION` it would be potentially much more accessible the the key used for encryption? Aren't there hence cases where encryption of `$_SESSION` helps to protect the data? – humanityANDpeace Mar 17 '14 at 17:17
  • This comment flys in the face of https://owasp.org/www-project-cheat-sheets/cheatsheets/Session_Management_Cheat_Sheet.html which states that if the data which could be stored is in any way sensitive it should be protected. – jas- Jan 21 '20 at 00:31
  • @jas- What it actually says is that the *session management repository* should be "encrypted and protected" if it is expected to contain any sensitive information. However, it does not elaborate and leaves you to build your own threat model. In order to get to the session data the attacker must have already compromised the server in some way, so any protection against that is inherently a protection of the session repo. The session directory is owned by the web user (e.g. `www-data` for Apache, `nobody` for nginx), usually with 0600 permissions, which is an additional access control. – Polynomial Jan 21 '20 at 14:27
  • 1
    @jas- In terms of the encryption side of the threat model, we've already established that an attacker would need to gain access to the server under the context of the correct user to read the session database. As such, any encryption we would do inside PHP (or whatever other language you're using) would necessarily require that the web server could access the keys, meaning that an attacker who compromised the server would *also* be able to access the keys, rendering the session encryption pointless. – Polynomial Jan 21 '20 at 14:30
  • 1
    @jas- Where encryption is useful is in the scenario of disk theft, but encryption *only* works here if you're not storing the keys on the disk too. This means that any encryption you do at the application layer isn't useful, because an attacker who steals the disk can just read the keys off the disk. Instead, you need full-disk encryption (FDE) on the server, with the key being manually entered at boot via remote management (e.g. iLO, DRAC, remote KVM). This is a default recommendation that transcends the issue of session management. – Polynomial Jan 21 '20 at 14:35
  • 1
    @jas- That being said, I agree that in hindsight my answer was poorly written (the benefit of 8 years additional experience!) and I'll do a rewrite to cover more of the details. – Polynomial Jan 21 '20 at 14:38
9

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.

D.W.
  • 98,420
  • 30
  • 267
  • 572
  • 3
    So to summarize, there's NO need for encryption if there's NO sensitive info in session data, whether on shared hosting or private hosting. – IMB Aug 20 '12 at 12:51
  • 1
    @IMB, yes, that's correct. Thanks for boiling this down to a simple, clear rule of thumb! – D.W. Aug 20 '12 at 16:30
  • 1
    Any hosting company worth dealing with will have the Hardened PHP patch installed which encrypts the session content anyway transparently to the user, which renders a lot of your points somewhat moot. – SDC Aug 22 '12 at 08:51
  • 1
    @SDC besides "kindly asking them" is there a way to tell that a certain php setup has such a patch and be sure that potentially sensitive data is really encrypted in this magical "transparent way"? – humanityANDpeace Mar 17 '14 at 17:22
  • 2
    I think blaming this on PHP, and suggesting encryption as the solution, is a bit far-fetched. The problem surely is that the shared host is poorly configured such that users can easily read each other's data. PHP provides plenty of configuration variables the host should be setting to jail this data appropriately. – IMSoP Oct 12 '17 at 14:22
  • @IMSoP, that's reasonable! I have no arguments with that. – D.W. Oct 12 '17 at 15:41
4

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.

symcbean
  • 18,278
  • 39
  • 73
  • +1 the mentioning that the session data could for many reasons persist! Especially when PHP-sessions is used to store sensitive data encryption using a key directly implantet in the script would keep only accessible to the script, having access nonetheless. By ways of the servers/permission the session data might be more accessible/attackable then the script. – humanityANDpeace Mar 17 '14 at 17:10
1

It would be less complicated to use cookies to only store a session ID. The server must then remember which user belongs to which session, and which permissions each user has. This design has the disadvantage that it makes load balancing less effective because session data has to be communicated between all application instances.

A better design would be to let login nodes perform authentication and look up user permissions in the database. This data is then handed back to the client as a cryptographically signed token, which can be stored in a cookie or in a URL. Signing this token is required since encryption does not inhibit tampering with the token. It is critical then that each service verifies the signature before acting on it. This is actually the core idea between JSON Web Tokens

MauganRa
  • 159
  • 3