3

I'm looking for a way to have a website remember sensitive data, but without actually storing it server side. And I was looking at HTML5 localStorage to do it. Here's the plan as I see it.

  1. User enters sensitive data into form, and submits.
  2. Server encrypts data via AES-256 with a strong key that is kept in private source control.
  3. Server responds, providing encrypted data to rendered page.
  4. Page runs javascript to save encrypted data to localStorage

Later...

  1. User visits a page that uses javascript to fetch encrypted data from localStorage and sends it to the server.
  2. Server decrypts encrypted data, gaining access to sensitive information for that request.

My thinking is that that allows either the client or server to compromised, and things stay secure. If the client is compromised, then the hacker can read only an encrypted string they do not have the key to decrypt. If the server database is compromised, the data is simply not stored there so it obviously can't be accessed by the hacker.

(Obviously some types of server hacking could read sensitive data as it's coming in initially, but such a hack would work whether client storing it or not, so that doesn't apply to this discussion. Also client hacks that log keys and whatnot would still work, I'm simply talking about data storage getting compromised on either side here)

But I'm no security expert, so I am wondering if my plan has any holes in it? Any glaring security vulnerabilities I am missing here?

6

There are two big problems with this.

  1. It ties the data to that particular browser installation. So someone cant log in from a different location and access it. Or even a different browser on the same machine. That defeats the purpose of most web apps. Also, what about public machines? You'd be storing sensitive data, albeit encrypted, on publicly accessible machines.

  2. You have to be extremely careful validating the data that gets sent back to the server. Once its out of your control, you cant assume anything about it.

And then after that, your server can still be hacked, even if the data isnt compromised.

  • 1 is fine. It's not critical its remembered, merely convenient for returning users. And would of course give people the option to opt out. As for 2, how so? Without the original encryption key, there is no way that anyone would be able to read or alter the data as any alteration simply would not validly decrypt, right? And given that this is data directly provided by the user in the first place, alteration is not really a concern. Hiding form a third party is the concern. This is not stuff like user id's in the session, just user provided data. – Alex Wayne Jun 13 '12 at 19:09
  • 3
    For #1, I have no way to know if its critical or not, as you didnt provide any details about the application or what is being stored in your question other than calling it 'sensitive'. But I know most users would not get the warm fuzzies from knowing a site was storing their credit card number or any personal information, encrypted or not, on a public machine. For #2, if its 'impossible' to decrypt, then whats the problem with storing it on the server encrypted and completely removing #1 as an issue? – GrandmasterB Jun 13 '12 at 19:58
  • 4
    Also, when you say "is not stuff like user id's in the session, just user provided data.", consider that ids and session info is whats not important - its the user's information that is most important to the user. – GrandmasterB Jun 13 '12 at 19:59
3

I'm looking for a way to have a website remember sensitive data, but without actually storing it server side.

Use the server to store encrypted data, there is no reason to use localStorage. It is better to let the data be encrypted by a passphrase client-side. Your method does not secure the client at all, because the weakest link is typing in the passphrase client-side.

What you basically want is the following:

  • User has sensitive data: "I stole candy".
  • User types in a passphrase.
  • Javascript consumes the passphrase, and calculates a slow hash (e.g. scrypt, just any proper key stretching algorithm).
  • Javascript uses hash to encrypt the sensitive data.
  • Javascript calculates another hash from the given slow hash.
  • Javascript submits the double hash and the encrypted data to server.
  • Server calculates a hash of the received double hash.
  • Server stores the triple hash and the encrypted data.

Now there is a break, all cookies/localStorage/sessionStorage is cleared.

  • User comes back, enters passphrase again.
  • We calculate a double hash again, and check with server who calculates another hash and tries to match with existing triple hash. This way the user is securely authenticated to return the encrypted data.
  • Then the encrypted data that belongs to that triple hash is returned, and the single hash of the passphrase is used to decrypt data.

No matter where the data is stored, nobody can access the sensitive data without the passphrase. Only the user should know the passphrase. If needed, the user can share his decrypted data with the server anytime. But if you are concerned about your server being compromised (which seems to be the case), I would do the processing of the sensitive data client-side.

Since the passphrase is in the user's head, and the encrypted data is stored on the server, the only way to hack the sensitive data is by intercepting the client keyboard or brute forcing the passphrase.

Hence there are a few important assumptions:

  • You've used salt in every hash.
  • Keystretching hash is future-proof and slow enough.
  • The passphrase has enough entropy (so it is hard to guess/force).
  • The connection with the server is HTTPS.
  • The passphrase and singlehash are destroyed after use, and are not used in a globally accessible scope. Note that typing the passphrase is the weakest link.

Here is an example:

# CLIENT
singlehash = keystretcher(passphrase + salt1)
ciphertext = encrypt("I stole candy", singlehash)
doublehash = fasthash(singlehash + salt2)

         |
         |
       HTTPS(ciphertext, doublehash)
         |
        \ /
         '

# SERVER
triplehash = fasthash(doublehash + salt3)
store(ciphertext, triplehash)

And when the client comes back:

# CLIENT
singlehash = keystretcher(passphrase + salt1)
doublehash = fasthash(singlehash + salt2)

         |
         |
       HTTPS(doublehash)
         |
        \ /
         '

# SERVER
ciphertext = fetch(fasthash(doublehash + salt3))

         |
         |
       HTTPS(ciphertext)
         |
        \ /
         '

# CLIENT
decrypt(ciphertext, singlehash) == "I stole candy"

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.