6

I realize this is often described as a bad idea. So first of all I'll state motivation - feel free to critique this.

Creating a product for an industry which tends to be overly concerned with security. The product needs to store and return user files but they do not need to be readable server side.

I feel that by encrypting client side would reduce the trust required from clients. Their confidential data would only be touched by JavaScript which they could inspect and monitor. It also mean any security issues server side which would allow access to stored data do not result in client documents being available (exception being if they can modify served JS).

My ideal solution would be -

- Key generated and injected to all clients users by clients IT team.
- JavaScript uses key to encrypt documents client side. These are then sent to the server and stored.
- On retrieval documents are decrypted again client side.

There seem to be several libraries to support client side crypto. But Currently I see no way to persistently store a key for the user. Ideally this would be in a similar fashion to adding certificates to the browsers store - i.e. they can be injected once.

Does anyone have any suggestions?

  • It's more secure to store the key on the server-side since e.g. malware can read your keys from your local storage. – Aria Sep 13 '17 at 17:49
  • 4
    @Aria If malware on the client can steal the key stored on the computer, then it can steal the key or the whole file when it's downloaded from the server too, or it could steal the user's session cookies, etc. – Macil Sep 13 '17 at 22:23
  • @Aria - as AgentME points out these documents will be uploaded from and retrieved to the clients machines. The clients machines are within their own control from a security perspective. – Hector Sep 14 '17 at 10:16
  • don't store the key, have the user enter a password and derive the key from that. if the user wants the browser to save the password for them, no big deal, and it +(likely) be done safely in newish browsers, outside the reach of xss. – dandavis Sep 15 '17 at 6:12
4

(To be clear: this only protects users in situations that the attacker only gets read-only access and protects users who haven't used the site since an attacker replaced the javascript with a malicious version. It does not protect users from you being malicious from the start unless the user painstakingly verifies / records all javascript that your site delivers them. If that last part is important to you, there might be a way to use ServiceWorkers to alert users whenever a new version of your site's javascript is executed, etc, but that's for another question.)

You can store encryption keys locally in the client with localStorage or IndexedDB. The encryption keys could be strings that your client javascript uses.

On supporting browsers, for increased security you could use the Web Crypto API (+ IndexedDB) instead. With the Web Crypto API, you can generate keys in javascript and use them for encryption/decryption, but the javascript code can not get access to the raw key material no matter what. That means even if an attacker gets access to your server one day and replaces the javascript with a malicious version, they can not replace it with a version that leaks users' keys. (However, they could replace it with a version that makes clients download their files, decrypt them, and upload them back to the server in the background on the next time they go to the site. This would take time and could be noticeable to users and/or you, so it might give you enough time to patch the issue before all user data is leaked.)

2

If using asymmetric encryption from openpgp.js, the private key is stored encrypted by default and then briefly decrypted to decrypt or sign a message with the users passphrase.

It is never stored un-encrypted and relies on the user remembering the passphrase he used to create the key pair.

In this case even if the hacker found the private keys they are useless without the passphrase.

Downside is we have to train the user to safely store his passphrase somewhere!

1

If you control server side source code:

My suggestion is that you store at least 4 keys on the backend which you rotate and user symmetric encryption server side then do the all encryption and decryption server side.

I had similar use case where my workflow was (with fernet encryption):

  • User uploads data then its encrypted with 1 of 10 (rotated) keys then stored in database

  • On query after I got if from DB then decrypt it and sent it to frontend

Advantages:

  • More secure then handle any keys on frontend

  • If database got exploited via SQL injection it isn't plain text

Disadvantages:

  • Need more server side resources as it slows up the process

  • Keys are still plain text server side so if the host machine got compromised no help there but still attacker as 1 step more to get to data.

If you don't control server side code:

The best you can do is to store symmetric key in the database and on user registration generate one for each. After login save it in cookie as session for that user

Advantages:

  • Easy to implement

  • Each user has it's own symmetric key if one user-account gets compromised the other users data can't be decrypted

Disadvantages:

  • Cookie can be stolen so no iframe header is an must have

  • If not used HTTPS and HSTS mitm attacks can easy get the data

But don't take my suggestion as holy grail wait and research more sources then it's on you to decide. ;)

1

I feel that by encrypting client side would reduce the trust required from clients. Their confidential data would only be touched by JavaScript which they could inspect and monitor.

This isn't really true. No normal person can actually inspect and monitor javascript the browser downloads from a site, and if I trust the server / the server admin / the server software developers enough to believe that they don't send me javascript with a backdoor, I might as well trust them to store my files.

I agree that it's an additional layer of security, but it's one that's so easily stripped away that it seems almost pointless to implement it. It does protect against some threat scenarios - for example it probably protects backups or old hard disks that aren't wiped from leaking the customer's data - but it really doesn't reduce the trust clients need to put in the service provider by much.

But Currently I see no way to persistently store a key for the user.

If you don't want to store it in client-side browser storage, you might

a) Have the user provide the client javascript with a passphrase, symmetrically encrypt the key with the passphrase and a strong symmetric cipher and send the key to the server to be stored there

b) Display the key as a QR code and have the user take a picture with his/her mobile. When you need the key back, the user can copy-paste the key string the QR code resolves to back into the client-side javascript app. That's very cumbersome, though.

  • You can automate notification of change in script files to flag for audit. Of course actually auditing the JS becomes difficult - there are numerous ways to inject in additional code in ways that are hard to foresee. But simply having the ability to do so can offer the client a level of confidence. As for trust it reduces the attack vector. To compromise client files from the server you need to obtain write access to the application server (or exploit the server code itself enough to return JS from RAM). Also worth noting this is as much as anything reassurance for non-technical users. – Hector Sep 14 '17 at 10:21

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.