0

The password of the user shall be used for client side encryption. A PBKDF and salt shall be used to derive a key from the user password.

Do I need to store this salt on the server and deliver it to each new client/device the user authenticates from? (I'm assuming here that the client can't store the salt, otherwise he could just store a strong encryption key and using a PBKDF would be a bad choice.)

If I do have to store it in some form on the server, is there a way to make it "hard" for the server to guess a weak user password? I guess the answer to this question is "no". It would always be possible for the server to try logging in as the user and brute-forcing it's way through a weak password in this way.

1
  • You might want to look at SRP protocol, which is similar to what you describe in that the user's salt is stored on the server and used for password based key derivation on the client side. See protonmail.com/blog/encrypted_email_authentication for an interesting read on how ProtonMail does this - and derives not only one key used for client side encryption, but also a second key used for authentication with the server, from a single password provided by the user.
    – mti2935
    Dec 30, 2021 at 18:39

2 Answers 2

1

Do I need to store this salt on the server and deliver it to each new client/device the user authenticates from? (I'm assuming here that the client can't store the salt, otherwise he could just store a strong encryption key and using a PBKDF would be a bad choice.)

Yes. Although I would probably store some random data in the server, then let the user transform it with its password to get a different random data, which is then the one used as key by the client.

If I do have to store it in some form on the server, is there a way to make it "hard" for the server to guess a weak user password?

No. The server could always "fake" a login by the user. Moreover, it could simply log the password provided by the user to decrypt their secrets later-

I would suggest that when the client provides the password to your website for login, it is actually some salted version (e.g. with a "website" salt) different than the one it will use for their secrets. Not perfect, but a small improvement.

2
  • @mti2935 mentioned SRP. As I do not want to roll my own crypto, is SRP an algorithm that "stores some random data on the server and lets the user transform it with his password to derive the key"?
    – Gamer2015
    Dec 31, 2021 at 7:47
  • @Gamer2015 SRP works in a different way. See en.wikipedia.org/wiki/Secure_Remote_Password_protocol
    – Ángel
    Jan 12, 2022 at 1:08
0

Sounds like you're trying to build a zero-trust system. I applaud you, but bear in mind that they're tricky! Especially for multiple-client systems where the only form of authentication is a password (or possibly password + some second factor), you need to keep a few things in mind:

  • To answer your question directly, the salt for deriving the user's key is going to need to be either stored in the server, or already known to the user (e.g. the user's username/email address). The latter is less secure - if it's known to the user, it's likely known to an attacker too - but that's still sufficient to prevent using a generic rainbow table (a custom one could be pre-computed for a specific user, but this doesn't scale and attackers couldn't use existing tables) or for two different users with the same password to end up with the same key.
  • You must NEVER send the user's password in plain text to the server. That means you must pre-hash the password entirely on the client, and send a client-computed hash to the server, for authentication. Of course, this pre-hashing should use a salt as well, but since it's pre-authentication, the server can't very well send you a salt that nobody else can find out. So for this step, you're always going to be using a publicly known (or at least knowable) salt. Make sure it's still unique for every user!
  • The key derivation function must not iterate forward from any state that you send the server (the un-hashed password is just a special case of this rule, actually). Either compute the key first and then derive the authentication hash from it (requires that the key be hashed using a public salt, though you can always re-hash it with a salt from the server after auth to get the "real" key if you want), or authenticate, get a different salt, and re-run the KDF using the new salt to produce the key. Never just re-hash any value that you sent to the server as part of the derivation.
  • Obviously you still should re-hash the hash on the server, before authenticating the user. The hash that the client sends is password-equivalent, and should be treated like one.
  • Because the key derivation is client-side, you can (and probably should) use a more expensive KDF than you would use for a purely server-side system, because there's much less risk of DoS (the server doesn't have as much to do). However, some users are on very low-power devices (old phones, netbooks, embedded / IoT systems, and other low-performance hardware). As such, try not to expect them to be able to run the algorithm as fast as your dev machine can do it.
  • PBKDF2 is a solid construction when all you want is to turn up the compute cost, but its extremely low memory requirement makes it very vulnerable to parallelization. A GPU can test thousands of candidate passwords in parallel at full speed; a CPU can probably test any given candidate faster than the GPU can, but even the highest-end CPUs only have a few dozen cores, not the thousands that a modern GPU has. Bcrypt is a little better here (its memory requirement is low by today's standards, but it's still somewhat slower on a GPU due to the extra I/O needed), but in general, if you can use modern algorithms such as argon2 (which has a tunable memory requirement) I recommend doing so. Of course, if PBKDF2 is available at native speed through a browser API and everything else has to run through the JS engine, that might tilt thing back to PBKDF2.

There are lots of other considerations around zero-trust systems - what do you do if a user forgets their password, do you ship a client app to get around the host-based security problem that makes zero-trust on the web mostly meaningless, what if anything do you store persistently within the client post-authentication, etc. - but those are the ones I'd be most worried about given your question.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .