11

I have read bunch of answers and tutorials on how client side cryptography is not a good idea because of many reasons listed mainly in Javascript Cryptography Considered Harmful article. Some facts

  1. The app will be using HTTPS
  2. There will be server and client side validation
  3. There is no link between user's password and the following scenario
  4. The server side is a RESTful web services (no session whatsoever)
  5. The backend issues JWT to users
  6. The backend might allow both user/password as well as oauth (i.e. fb, twitter, etc.)

What do I want to encrypt in client side?

The user will be inputing some personal information (i.e. name, address, phone number) via a frontend app (i.e. Vue.js, Angular, React, etc.)

How do I imagine this is going to work?

The user selects fields (i.e. personal information bits and pieces), and then clicks encrypt. The user will provide an encryption key. The frontend will use some js crypto library and encrypt the information and replace them in the DOM and inform the user to backup the encryption key.

How do I communicate this information to server?

The frontend will make a HTTP PUT or POST request with the personal information (whether plain/text or gibberish) to the server. The server don't care about incoming data (i.e. except for sql or other forms of injections) and store it in the database right away. This request will not contain the encryption key but, might contain a flag/s to specify that which fields were encrypted.

How to decrypt?

When the user requests for the information, the backend returns whatever there is in the server as is. The information if encrypted will be displayed as gibberish to the user. Using flags, the system will give them decrypt option and clicking that would ask them to input encryption key and does the decryption locally.

Ultimate purpose?

First of all, every effort will be taken to make sure that server and database and everything is bullet-proof but, if for some reason the server is hacked then the hacker would end up with loads of information that is tied to gibberish personal information.

The ultimate question

Reading through internet, cryptography in Javascript is strongly discouraged hence, my questions

  1. Taking into account the scenario and requirement above, what are the risks imposing the above implementation?
  2. Is there an approach that you recommend, where only the user is in control of the key, not stored anywhere?
  3. If I were to encrypt this information in the backend, how can I communicate the encrypt key safely with the user? Maybe encrypt the information using key xyz and then provide a secure link to the user to click and view the xyz encryption key, ... ?
  4. What other suggestions are there for front-end, backend-based encryption?

I would like to give the user's the confidence that only they know how to decrypt the key. How can I achieve that in a more efficient way? Without having to store and manage the encryption key?

Update July 17, 2018

Based on the accepted answer, I wrote a medium post on how I implemented the solution here is the link https://medium.com/@rhamedy/encryption-decryption-of-data-based-on-users-password-using-pbkdf2-aes-algorithms-592f8c1bb79a

Raf
  • 221
  • 1
  • 2
  • 7
  • 1
    "_cryptography in JS is strongly discouraged_"; no that was 10 years ago, those points are stale and outmoded. – dandavis Jun 15 '18 at 17:59

1 Answers1

12

Upon initial user registration, generate a strong encryption key using a cryptographically secure pseudorandom number generator; this key will serve as the Data Encryption Key (DEK) to encrypt the user's data. But you don't want to store the DEK anywhere, at least in its plaintext form. You want to derive an encryption key from the user's password, using a key derivation function such as PBKDF2. Use the key that you derived from the user's password (the Key Encryption Key, or KEK) to encrypt the DEK. Then you can safely store the ciphertext of that DEK in the database.

Each time the user subsequently logs in, you must not only authenticate them but also derive the KEK all over again from their password, then use that derived key to decrypt the ciphertext of the DEK. Store the DEK in the session on the server, not in the client. Now you're in business; you can use the DEK to decrypt information on the server side for as long as the session is active, but not after the session expires. This has the advantage that the user doesn't have to stare at some gobbledegook ciphertext. You can instead have a placeholder container DOM element saying "encrypted" or something, and maybe listen for clicks on that element, which then invoke a call to an API endpoint, which grabs ciphertext from the database, decrypts it on the server with the DEK that you stored in the session, then returns it to the client as plaintext right then-and-there, where you can inject it into the UI for the user to see. No sensitive data need be stored on the client, nor is it necessary to perform any cryptographic operations on the client. This approach also has the added benefit that the DEK can stay constant over time. If the user ever changes his password, just derive a KEK from the new password, and re-encrypt the same DEK as before with the new KEK, and store the new ciphertext of the DEK in the database as before.

vrtjason
  • 1,045
  • 9
  • 10
  • Thanks for the answer. It looks like there is a lot for me to learn. One thing I wanted to point out is that, the backend is Restful web services (no sessions) and issues JWT token to authenticated user that could either be username/password or social OAuth any comments on that regards. Your answer definitely gives me some direction on where to research further. In your point of view, what are the flaws with the method I described above (generating key and doing the encryption in the frontend app)? Would appreciate your views. – Raf Jun 15 '18 at 17:49
  • One weakness of encryption on the front-end is that you might have malware or cross-site scripting that are listening in on the front-end activity, possibly intercepting it, even transforming it. Also, the front-end encryption libraries that you might use may have weaknesses in their implementation that could enable exploits on the front-end. Plus, data-encryption keys require cryptographically-secure sources of randomness, and the front-end is a completely inadequate substitute for that. Randomness should be generated by physical phenomena like ambient heat. JS simply can't achieve this. – vrtjason Jun 15 '18 at 22:48
  • That's very useful, thanks a lot. I suppose I am going to do an even more in-depth research on how I could achieve this in the backend with anonymity in mind. – Raf Jun 20 '18 at 13:36
  • I wrote a medium post on how I achieved this based on your answer and some more research. Whenever you have time give it a look (updated the question with link). – Raf Jul 17 '18 at 23:16
  • @Raf: Great post! Very thorough and well documented. – vrtjason Jul 26 '18 at 16:59
  • Just as a side note: This technique is used in P2P Encryption in Credit Card Payment terminals for encrypting the transaction information. So even if you crack into 1 transaction, you can never decrypt/crack into any other transaction. Pretty neat, secure and amazing! – Aniket Inge Jul 17 '20 at 13:36