0

Context

I have a system where some of user's data is encrypted via AES. Each user has their own key K. When the user creates an account, the K is generated and encrypted with a key derived from password via PBKDF2 (let's call this key P). This P(K) is stored in database and taken when logging in to be decrypted by P again.

There's a token system with refresh token stored in database and in user's http-only cookie. Every refresh token in DB is stored alongside the data about the user agent so all the active sessions from all devices can be identified and invalidated from account settings via a sessions manager.

Problem

In normal case, refresh token is used to keep the user logged in even after closing the tab and opening it again. But with this password based encryption system it's impossible to have K without knowing the P which is derived from password. That means either K or P needs to be stored either in cookies or in local storage. None of these variants seem to be safe to me, so I came up with an alternative solution I would like you to evaluate.

My Solution

When the user logs in, a "session key" S is generated. We make S(P) and store it in the database on server alongside the refresh token. Store S in localStorage.

This way, if S gets stolen, it will be useless on any other device but the one it was created on since the server won't give S(P) to decrypt as there's no refresh token and/or user agent data doesn't match. Also it will be possible to invalidate from session management panel as the S(P) will be deleted from database just like the refresh token. Password change will also lead to invalidation. If the database gets comprised, S(P) is also useless without S.

Would this approach be secure enough and wouldn't it be just as unsafe (or safe) as just storing P in localStorage? Maybe there's a different less complicated approach (though I don't find this one too complicated)

1 Answer 1

2

You should use sessionStorage, not localStorage. Data in localStorage is persisted on disk as plaintext, whereas sessionStorage gets automatically cleared when the tab or browser is closed (which seems to be closer to what you want).

Besides that, your approach is only slightly more secure than storing P in Web Storage (sessionStorage or localStorage). If you assume that the attacker has client-side scripting capabilities and can read Web Storage, then nothing prevents them from rendering a log-in form which looks perfectly authentic but sends the password to an attacker-controlled host. Sure, this attack requires user interaction, but if it succeeds, all encryption becomes useless. Given the password, the attacker should be able to access the user account (unless there's a separate password for this) and have the server fetch and decrypt any of the stored ciphertext.

Your scheme in general has major limitations. I think it would be very useful to write down an actual attacker model where you clearly define different types of attackers and their capabilities. This should give you a much better understanding of what you can and want to protect the data against -- and what is out of scope.

For example, an inside attacker who can access the database and either manipulate or hook into the application code is able to fetch the plaintext keys and use them to decrypt the stored data. An attacker with scripting capabilities may be able to get the password, as explained above.

Another aspect you should consider is that users can forget their password. If this makes it impossible to ever decrypt the data again, that's obviously a problem. So there has to be some backup mechanism -- but of course it must not compromise the security of the overall system.

Ultimately, it may be more reasonable to ditch the encryption altogether. In your specific scenario, it's just very limited, complex, fragile and can give your users a false sense of security. Encryption makes perfect sense if, for example, you have a desktop application which can encrypt the plaintext client-side and only ever send ciphertexts to other hosts. However, in web applications, that's difficult. JavaScript code doesn't help, because it's still provided by the server and cannot be realistically checked for backdoors before each request. A browser extension may be an option, but users may not be willing to install one just for your web application.

3
  • Wouldn't stealing user's password open the door for the attacker to read all the data anyway unless there's two-factor authentication? Sorry but I fail see how it's connected to my question. Just to clarify: in the described scheme neither the password nor the encryption key for user's data is going to be stored in localStorage, only session key that is useless without the corresponding refresh token.
    – v_slav.b
    Mar 13 at 11:16
  • I understand that only the session key is stored in Web Storage. But as I've explained in my answer, an attacker who has found a cross-site scripting vulnerability which would allow them to fetch the session key from Web Storage might as well generate a perfectly legitimate looking log-in form on your site and grab the password. So storing S(P) rather than P or even the underlying password in Web Storage is a minor improvement against an attacker with scripting capabilities. But the entire scheme is still extremely vulnerable to XSS and just one user interaction away from falling apart.
    – Ja1024
    Mar 13 at 12:20
  • Once again, your problem is that you're theorizing about all kinds of protection mechanisms, but you haven't thought about what you're even trying to protect against. You need an understanding of what kinds of attackers you're dealing with and what their capabilities are. Then you can think about what you can (and cannot!) protect against.
    – Ja1024
    Mar 13 at 12:25

You must log in to answer this question.

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