3

I'm building a system where security is important and I'd like to store session details on the server in a session table. However, I'm concerned that if an attacked got hold of the database, they would have access to all session details for all logged in users.

How do I get round this - is it just to keep the session details encrypted and send a secret to the user as a cookie, meaning that on each request there is decryption. This will obviously slow down the user.

I can't store session details in a cookie on user's machine as this cookie could grow very large and may exceed 4kb (maximum request header size).

3 Answers 3

4

In general session information is not usually encrypted on the server side. It is generally assumed, as part of the threat model, that if an attacker gains access to the server then they already have everything they need. Session data itself is usually not any more sensitive than the rest of the contents of the database.

There are a few options:

  • Store them on the filesystem in plaintext. If your threat model suggests SQLi is more likely than filesystem access, this is an easy move.
  • Encrypt session IDs and data using the database's inbuilt encryption routines. This protects you against cases where a database backup is compromised, but won't protect you against disk theft or an attacker with SQL access.
  • Encrypt your session IDs and session data in the database, using a key set in a config file on the server somewhere. The benefit of this is that the attacker would need access to both the database (e.g. via SQL injection) and the filesystem at the same time in order to decrypt the session IDs and session data.
  • Encrypt session IDs and data using a randomly chosen key which is cached within the server daemon process (e.g. APC or memcached for PHP). This would be exceedingly difficult for an attacker to gain access to without full code execution on the system, but has the downside that all session data would be invalidated if you restart the server process.

I recommend the use of authenticated encryption, with separate encryption and authenticity. Pseudocode for encryption is as follows:

iv = secure_random(16)
encrypted = iv || AES-CBC-128(session_id || session_data, enc_key, iv)
auth_encrypted = HMAC-SHA256(encrypted, auth_key) || encrypted

Pseudocode for decryption is as follows:

mac = auth_encrypted[0:31]
encrypted = auth_encrypted[32:]
expected_mac = HMAS-SHA256(encrypted, auth_key)
if mac != encrypted_mac:
    session was tampered with
else:
    iv = encrypted[0:15]
    encrypted = encrypted[16:]
    decrypted = AES-CBC-128_Decrypt(encrypted, enc_key, iv)

This provides authenticated encryption to prevent tampering with session data on the server side.

3
  • 1
    What benefit does encrypting the session id confer?
    – symcbean
    Commented Jan 10, 2017 at 0:20
  • 1
    Why can't we store a session in a user's cookie or his/her browser's web storage?
    – Magnus
    Commented Oct 21, 2018 at 16:12
  • 1
    @Magnus Because then the session data is editable by the user.
    – Polynomial
    Commented Oct 22, 2018 at 13:48
1

The standard approach to this is to make sessions have a relatively short lifetime - the most common way for an attacker to gain access to the database is through obtaining backups and those sessions would be expired in that backup.

If an attacker has live access to the live database, they likely don't need sessions, so it may not be such a worry.

Alternatively, if you do not need to share sessions across servers, you could simply hold sessions in memory. (Most default session implementations do this in most frameworks I am aware of)

An alternative to sessions might be JSON Web Tokens - those also have a lifetime, but they are self-contained - they contain claims (ie, I am user X) and are validated either by signing or encryption. That requires that you either have a central auth server that signs all tokens (which other servers read and can verify) in a public/private key setup or that all servers have access to a common encryption key (which obviously must be protected very well - HSM or equivalent is often used for this). Since they are passed back and forth by the client, you do not need to store them, since you can verify them each time the client does an action.

1

This will obviously slow down the user.

No it won't.

Yes, it will be slower than storing plain text, and it will reduce capacity but the difference will be negligible (unless you're very bad at this).

My encrypting session layer costs around 0.002 seconds per request (4k session size) on fairly run-of-the-mill hardware with 256 bit AES. Human beings can't perceive differences in duration below around 0.030 seconds.

If we're talking specifically about PHP, then note that there is also a cost of approximately 0.0005 seconds per request to use a custom serializer - required for my handler. OTOH there are other things you can do with a custom handler to improve capacity.

maximum request header size

This is not limited by HTTP - it may be a local configuration constraint. Certainly some browsers limit the size of cookes.

I can't store session details in a cookie

No, but you can store more data in multiple cookies and structured storage on the client (there are libraries to handle this). But it adds a lot of complexity. And as explained above, there is no net benefit.

1

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .