• caglararli@hotmail.com
  • 05386281520

Encryption of localStorage/indexedDb with server-side PBKDF2 derived secret secure?

Çağlar Arlı      -    38 Views

Encryption of localStorage/indexedDb with server-side PBKDF2 derived secret secure?

Hello InformationSecurity community!

I have the following situation, and seeking for advice for our security architecture.

I am working for a client, who creates a resume builder app, where users can enter their details (e.g. email, phone numbers, first&last name, job experiences and other resume related stuff) into the website, press the download button, and get the CV as a file download.

Currently we save the user's details in localStorage and indexedDb, and not on our servers. Our current implementation encrypts all data on the client with a server- generated encryption key, which is not stored on the client. However, I have doubts since I know that localStorage (and basically all other storage types on the client) are vulnerable to XSS attacks. Thus the question here :)

The data persistence process currently works the following way: Assuming the user is logged in in our application. By encryption/decryption I mean: AES-GCM

  1. Client makes a request to our server to fetch encryption/decryption secret. Request includes the Auth token
  2. Our server verifies the Auth token signature. If the signature of the token is valid, the server extracts the userId from the token
  3. Our server generates a randomKey of a length of 32 bytes
  4. This random key+userId in combination with a server-side secret are used to derive an encryptionKey. encryptionKey=PBKDF2(PBKDF2(serverSideSecret, userId), randomKey)
  5. This encryptionKey and the randomKey are returned to the browser
  6. The randomKey is persisted to localStorage, the encryptionKey remains in a private variable of an ES6 module, which is not exported. The encryptionKey itself is not persisted to any client storage
  7. The data is encrypted using the key, the ciphertext is saved in the localStorage of the browser.

The retrieve from the storage:

  1. The random key is read from the localStorage
  2. Client makes a request to get the decryptionKey for the data. The request contains the Auth Token, as well as the random key from step 1
  3. Server validates token, if valid generates the decryption secret from the userId: decryptionSecret= PBKDF2(PBKDF2(serverSideSecret, userId), randomKey)
  4. The decryption key is returned to the client, who can decrypt the data
  5. The DOM is populated with the encrypted data. The user can continue to edit it

The main point is, that my client does not want to persist any client related data server- side. By using the process above, basically every time the data is persisted to localStorage, it will be encrypted with a new encryptionKey (since every time a new randomKey will be generated), which can be fetched only from the server with a valid Auth token.

But I have the feeling there are some security holes. Am I right?