Browser: Encryption Key Storage

Created on 2 Oct 2016  路  5Comments  路  Source: bitwarden/browser

bitwarden never sends unencrypted data to the server. All data is kept on the client machine and decrypted during runtime using the master password as the key. For the browser extension, all vault data is stored using the chrome.storage API. All sites and folders are stored in their encrypted form in chrome.storage. chrome.storage is considered an unprotected data storage medium since it's just plainly on the client disk. The chrome.* API for web extensions does not provide a secure way to store data (for example, like the iOS Keychain).

Currently the browser extension also stores the encryption key in chrome.storage. This could be considered a security issue since anyone with access to the client machine could access it. I see no other alternative to securely store this data.

One option would be not to store the key at all, keeping it only in memory, however, that would require the user to re-enter their master password every time their browser was restarted. Although this could be made an option for more security aware individuals, this does not seem to be feasible from a normal user experience.

From what I can tell, this is an issue with any web extension. It is also an issue with the default password storage that browsers like Chrome and Firefox do out of the box. The consensus seems to be that if the nature of your extension is to store sensitive information on the client, then users need to be taking proper security measures to keep their machines safe (locked with password, anti-virus, etc).

Other options? Suggestions? Comments?

discussion

Most helpful comment

Storing in memory would seem to me to be a much better option.
Anyone using firefox sync (example) for password already needs to type the master password when the browser restarts and it is not that big of a deal, especially considering the security gain.

All 5 comments

Storing in memory would seem to me to be a much better option.
Anyone using firefox sync (example) for password already needs to type the master password when the browser restarts and it is not that big of a deal, especially considering the security gain.

Overview

Well this idea would require the user to always enter their password in offline mode, but what if you encrypted the encryption key in chrome.storage with yet another key that was stored on your server. This way you wouldn't be storing their password or anything. In fact you could probably use hmac with a rotating secret also stored somewhere in chrome.storage so the keys in your db wouldn't directly decrypt anything either -- not sure that really add any security though.

Use Cases

Bear in mind I haven't really gone over your architecture / work flows so I'm sure this will have to be modified some.

Initial login

  1. User supplies password and decrypts master pass successfully
  2. Client sends request-for-encryption-key message to the server along with client id (to allow various browsers/oses/etc to each independently encrypt their local encryption key)
  3. Server generates a random secure string

    1. use securerandom to just generate a 256 char string or something

    2. also generate a verification string

    3. saves it in the database along with the account and client ids, and maybe a timestamp of when it was generated

    4. the secure string and verification strings are sent back to the client

  4. Client stores the verification string in local storage
  5. Client generates their own secure string and stores it somewhere in storage
  6. Client uses hmac with the provided key from the server as the message and their privately stored secure string as the key to generate a new encryption key
  7. Encrypted key is encrypted with the key from the previous step and stored into storage

Browser startup -- decryption attempt

  1. Client recognizes that the encrypted key is stored locally
  2. Contacts server requesting the previously generated key for their client id and verification string
  3. Server looks up entries in db to find existing entry
  4. No entry is found or verification string doesn't match:

    1. Return no-key-found message to client

    2. Client flushes the stored encrypted-encrypted-key, verification key, and hamc-key

    3. Client performs steps in Initial login use case

  5. Entry is found:

    1. Server could do additional checking here like:



      1. Maybe look for login country and only return if it's the same country they requested the enc key from initially


      2. See if there is too much activity from that IP -- would have issues with corporations, proxies, public wifi, etc... maybe basic ip reputation from somebody like proofpoint or something would work? Probably more trouble than this is worth honestly. Maybe somebody has better ideas here.


      3. Do some sort of timeout where the user is forced to re-enter password. Could be client specified option maybe? Also maybe have a way for the client to behind-the-scenes "renew" the password and refresh the expiration date?


      4. Implement 2-factor auth at this stage if the user wants it



    2. Any check from above fails:



      1. Send message regarding why it failed


      2. Client flushes the stored encrypted-encrypted-key, verification key, and hamc-key


      3. Client displays "failure" to user and proceeds to Initial login use case



    3. All checks passed



      1. Send stored secure-string to client


      2. Client looks up mac key in storage


      3. hashes the received secure string with the locally stored secure string as the key


      4. Decrypts the locally stored enc key and proceeds as normal



Other notes

I suppose in theory you could provide the user with a _Warning! Danger!_ option where you could still store the encryption key locally so offline mode would work... how much rope do you want to give them exactly?

Might be worth it to store where the decryption asks come from as well so if you eventually detect some suspicious behavior from ip x you can just invalidate anything that they touched and/or contact the users with a warning.

I just typed this out without giving it too much thought and given the sensitive nature of the subject should definitely be given some more serious thought.

This is just a general observation, but: no password manager that I have used has a UX that allows unchallenged access to encrypted content after a fresh start of the app. I like this, because it means if I quit the app, I don't have to stop and think about if I locked it or not.

So, when I read:

One option would be not to store the key at all, keeping it only in memory, however, that would require the user to re-enter their master password every time their browser was restarted.

The immediate thought in my head is that this sounds like a feature, not a regression. I would actually like to be able to enforce this behavior even if the tab so much as closes.

Adding in the option for not storing the encryption key should be pretty easy to implement. I'll look into prioritizing this and creating a formal issue for it.

This has now been resolved with the new "lock options" feature of v1.2.0. Setting the lock options to anything other than "Never" will result in your encryption key only being held in memory of the browser session. It will no longer write to disk using chrome.storage.

Also note that when your extension is in a locked state (signified by the red lock on the bitwarden extension icon) the encryption key is also purged from memory (which is why you must enter in your master password again to unlock).

https://github.com/bitwarden/browser/releases/tag/v1.2.0

Was this page helpful?
0 / 5 - 0 ratings