As a bit of a thought experiment, I've been thinking through how one might design a web application where a user's data (say, from a form on a webpage) is stored, encrypted, on the server, using a per-user key. The decrypted data is shown in a form on the webpage when the user logs back in.
I've come up with two potential approaches and I've attempted to identify the pros & cons of each, but I'd love to get others' thoughts - I'm certain I haven't thought of every possible attack.
Here are some properties of my hypothetical system that are consistent across all three approaches:
- Everything occurs over SSL.
- Any cookie that goes over SSL is marked as secure.
- Data to be encrypted: a single form field on a webpage, consisting of plain text
- Data is encrypted with a N-digit PIN
N-digit PIN is stored in DB on server (bcrypted or scrypted)
N-digit PIN is passed through password derivation function to generate 256-bit encryption key
Data encrypted using AES-256-CBC mode, random IV. Signed using Encrypt-then-MAC approach (e.g., some form of authenticated encryption). HMAC key is derived from N-digit PIN.
Option 1: Encrypt on server, with key stored temporarily in server-side session object:
- User logs in with PIN.
- Server authenticates against PIN digest in DB.
- Server passes PIN through password derivation function to generate 256-bit key.
- Server stores key in server-side session object (& persisted in Redis or memcached).
- Server renders form for user to enter data in (& sends cookie with session ID to client).
- User enters data in form, submits to server.
- Server encrypts data with key in server-side session object, signs encrypted data.
- Server stores signed, encrypted data in DB.
- User logs out / session expires
- Key is wiped from session & wiped from persistent cache.
Cons
- In a server-side attack, attacker would be able to get encryption key for any logged in user & would be able to decrypt their data.
Pros
- Key only exists temporarily on server. Once user logs out, persisted encrypted data is (relatively) safe in DB.
- Client-side attack doesn't get the attacker much - only a session ID in a cookie.
- In a server-side attack, attacker would only be able to attack logged in users - not sure how much of a pro that is, though.
- In a server-side attack, attacker wouldn't be able to get users' raw PINs.
Option 2: Encrypt on server, with key stored in encrypted cookie sent to client:
- User logs in with PIN.
- Server authenticates against PIN digest in DB.
- Server passes PIN through password derivation function to generate 256-bit key.
- Server stores key in encryped cookie.
- Server renders form for user to enter data in (& sends cookies with session ID and encrypted key to client).
- User enters data in form, submits to server.
- Server extracts key from encrypted cookie.
- Server encrypts data with key from client's cookie, signs encrypted data.
- Server stores signed, encrypted data in DB.
- User logs out / session expires
- Cookie is deleted locally on client's machine
Cons
- On the client, attacker has encryption key - but that's really all they have. They don't have encrypted & signed data, they don't have unique IV, etc.
- That said, if you're logged in (presumably with the PIN), you're already looking at the decrypted data. Having the encrypted key doesn't buy you anything. The encryption key doesn't work for other users' data (unless they happened to use the same PIN, obviously, but there is no way for an attacker to know that).
Pros
- Key is never persisted on server, and only exists in memory for fleeting moments. Persisted encrypted data is (relatively) safe in DB.
- In a client-side attack, attacker wouldn't be able to get users' raw PINs.
- In a server-side attack, attacker wouldn't be able to get users' raw PINs.
- In a server-side attack, attacker wouldn't be able to get any other users' encryption key.
- To do any damage, attacker would need to successfully attack both the server and the client, and the damage would be limited to a single user.
I completely understand that in security, there are always tradeoffs, and nothing is perfectly secure - but assuming that an attacker can get inside a server (e.g., cough, the NSA), it feels like a server-side approach where encryption keys are even only temporarily stored is risky.
I feel that counterintuitively, option 2 is more secure.
Am I right/wrong? Is either approach a Bad Idea (TM)? If so, what are some alternative approaches to consider?