This is a follow up on my previous question about Storing sensitive, sharable data for a company where several of the employees have access to the server.

This solution has worked well for our customer, and we're looking to make a v2.0 as a SaaS. We still think that the added security of encrypted data is worth while even when server access is limited to our staff.

The first version had encryption/decryption done by the PHP application, but v2.0 has implemented OpenPGPjs library in a Single-Page Application (SPA) talking to a backend application (API). All encryption and decryption is now done in the browser, and this helps to mitigate the threat of memory dump attacks and adds an extra layer on the network traffic (this is of course also running with HTTPS).

The first version also had two separate passwords, one for logging into the application, and one that was only the key to unlock the private key. Much like ProtonMail's original solution. We have, inspired by ProtonMail, also adopted a single-password login to our service - the current user process is as follows:

(I've used strong emphasis capital letters to define notations)

1. Registration

The user enters e-mail-address and password in the a form in the SPA, and the SPA hashes the password (PASSWORD) with bCrypt. The registration form is sent to the API with e-mail and password hash (DIGEST).

2. Application Authentication (Login)

The user enters their credentials, and the SPA sends a pre-auth request to the API. The API answers with the salt of from the DIGEST (technically its $algo$rounds$salt) and the SPA uses this to hash the PASSWORD and send the e-mail and DIGEST as an authentication request. (the plaintext PASSWORD is never sent to the API)

If the authentication is successful, the API adds two http_only cookies in the browser. One CSRF-Token, and one signed JWT-token. The API send a response to the SPA including the CSRF-token.

(All successive requests are made with the CSRF-Token in the HTTP headers, and the API is comparing the token in the cookie with the token in the header)

3. Key Generation

When the user first log in, they are prompted to generate a EC public-private keypair. They re-enter their PASSWORD, and this is then hashed with a newly generated bCrypt salt. The new password hash (KEY) is used to encrypt the Private Key of the newly generated Keypair (KEYPAIR) (also generated in the browser). The salt for the KEY, public key and encrypted private key are then sent to the API and stored in a database.

4. Encryption/Decryption

When the user stores sensitive data in the system, the data is encrypted with AES256 with a randomly generated key (ENTRY_KEY) and sent to the API. The ENTRY_KEY is encrypted with the user's own public key, and stored as a token for that user to that entry. If the user wants to share that entry with another user, they decrypt the ENTRY_KEY and encrypts it with the new users public key. Now both users can decrypt the entry with their own token, and updates to the entry will be visible to both users.

Currently the KEY is stored in LocalStorage with a 5 minute expiration. The KEYPAIR (with the Private key encrypted) is stored in LocalStorage without expiration. When the KEY is expired from the LocalStorage, the user is prompted to re-enter the PASSWORD.

5. Password Resets

Someone will forget their password. It always happens.

We can reset the authentication password with standard forgot-password-functionality, but information encrypted with the keypair cant be restored - unless its shared with someone, then they can request to get those entries shared back to them.


  1. Are there any security concerns with using the same password for both encryption and authentication? In a breach the attacker could possibly get two variations of the password (DIGEST and KEY), hashed with different salts. Any alternatives to this approach?
  2. Storing the KEY in LocalStorage feels wrong, but we also can't prompt the user for their PASSWORD for each action in the SPA. Is local storage entries deleted permanently on expiration, and is there any safer storage options?
  3. Any other flaws you might see? Application source code attacks and compromised client systems are concerns that we're looking at, and any input to strengthening the security of the application is appreciated.

This was a long post, and I did my best to explain as good as I possibly can, but if there are anything that are unclear - please add a comment and I'll update.

1 Answer 1


If you send $algo$rounds$salt to the user, and the user logs in with the password hash, which is then checked with the rest of the line, that password hash is plaintext equivalent. Someone that stole the password hashes could log in as anyone (not that it would be too useful on this kind of application, since without the actual password you couldn't decrypt the contents).

You allow username enumeration, but that's probably unavoidable. You could use fixed parameters like using the email address as salt and log in with HMAC(email || HASH(rounds, password)) but you lose flexibility: that would mean having the number of rounds as a hardcoded number you won't be able to change (as it would invalidate the credentials for everyone).

In a breach the attacker could possibly get two variations of the password (DIGEST and KEY), hashed with different salts. Any alternatives to this approach?

You could use two different algorithms, eg. argon and scrypt. Given that you are using good-enough salts, it's perfectly good.

As a way to improve the application security even more, you could use a [optional] browser extension to store the piece javascript dealing with the password (so that the server js can't be changed to leak the password instead of using it only locally), that could also allow you to fix the part about the expiration.

Overall, it looks like a good design. Any chance you end up publishing your application? :)

  • Thank you for your comments. I did not mention it, but the encryption part is just a module in the greater application, so it is indeed a security flaw that a stolen hash can authenticate you - I don't know how i missed that, but great catch. The browser extension is also a good idea, and we have been discussing that as an option. The SPA will certainly be published for security reviews and transparency, and the application as a whole will (fingers crossed) be released as a SaaS. Again, thank you, your answer is much appreciated.
    – Jørgen
    Commented Jan 31, 2019 at 14:53

You must log in to answer this question.

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