1

For a fun project I want to support the SASL Mechanisms for authentication, especially PLAIN and DIGEST-MD5.

My question is: how can I store the users' password securely if I need to support those two ways of authentication?

With only PLAIN auth it would be really easy, I just store the password with bcrypt and compare the user submitted password with the stored pw using the bcrypt_compare function.

But how can I store the password securely when also DIGEST-MD5 should be possible? Should I store the whole calculated response and use that also for the PLAIN comparison? Or is there some other way?

//Edit: Regarding the "fun"-project. At the moment it is a fun project but no one knows if it will be a non-fun project at some point. And I don't want to decrease the security just because it's a fun project..

Michael Weibel
  • 2,857
  • 3
  • 23
  • 25
  • just md5 the bcrypt hash for the digest-md5 version – Jacco Nov 23 '11 at 11:16
  • yeah I thought that too but the user who is connecting doesn't calculate it's response to the challenge with the bcrypted password. So the hash won't ever be the same. – Michael Weibel Nov 23 '11 at 15:21

2 Answers2

1

The DIGEST-MD5 specification tells you what a server needs to store for that authentication method:

3.9 Storing passwords

Digest authentication requires that the authenticating agent (usually the server) store some data derived from the user's name and password in a "password file" associated with a given realm. Normally this might contain pairs consisting of username and H({ username-value, ":", realm-value, ":", passwd }), which is adequate to compute H(A1) as described above without directly exposing the user's password.

...so all you need to store for DIGEST-MD5 is H({ username-value, ":", realm-value, ":", passwd }).

You could separately store a bcrypt hash to use for PLAIN authentication, or you could just use the DIGEST-MD5 value. If you stored separate values then you could allow your users to selectively turn off DIGEST-MD5 authentication, which would allow you to remove that (easily-bruteforced) information from the database for those users.

caf
  • 233,326
  • 40
  • 323
  • 462
  • Thanks for your comment. I think that's the way to got but somehow this is scary, isn't it? I mean, DIGEST-MD5 should increase the security of the authentication process (which it does successfully as far as I understand it) but in the same time it decreases the security of the stored passwords :S I'm trying to implement a jabber server that's why I'm asking it. It will be possible to disable DIGEST-MD5 but if someone connects without TLS and the server only allows PLAIN, the password is sent without any encryption over the wire :| – Michael Weibel Nov 30 '11 at 07:32
  • Also in the spec it says "There are two important security consequences of this. First the password file must be protected as if it contained plaintext passwords, because for the purpose of accessing documents in its realm, it effectively does." I plan to store the passwords in a database..so "securing" the passwords file will be somehow hard to do. – Michael Weibel Nov 30 '11 at 07:37
  • @MichaelWeibel: Right, it's a tradeoff. If you want a better mechanism than DIGEST-MD5 there's SCRAM. The "security consequences" wording is saying that if someone gains access to your password file, they can impersonate users *to your server*. – caf Nov 30 '11 at 08:47
  • (But note that `H()` is just a single round of `MD5()` so storing that value is not a great deal better than storing plaintext passwords; with modern GPU hardware those are trivially bruteforceable.) – caf Nov 30 '11 at 09:00
  • Ok. Understood this. Thanks for clarifying :) I'll take a look on howto implement SCRAM additionally. – Michael Weibel Nov 30 '11 at 09:27
0

Don't store the password in plain. Store the hash and compaire in both with the hash. You can easily get the hash with the plain password.

Here ist how to create a good hash:

function hash_password($password, $nonce) {
  global $site_key;
  return hash_hmac('sha512', $password . $nonce, $site_key);
}

See Secure hash and salt for PHP passwords

Community
  • 1
  • 1
PiTheNumber
  • 22,828
  • 17
  • 107
  • 180
  • Yeah storing it in plain is not an option of course. I thought about using bcrypt for it. When I use your method, I need to store the nonce in the db too, or do I miss something? Because when I generate the nonce each time newly, it won't be the same of course ;) – Michael Weibel Nov 23 '11 at 08:17
  • yes, that is right. You store the nonce/salt and you have a site_key in you PHP file. – PiTheNumber Nov 23 '11 at 08:21
  • @owlstead What do you mean? Following the link 111 people think this is a good solution. Sure in the end it is always degraded to the hash function but here you are using one of the stronges hashs. Unless the database and the source code gets stolen it will be hard work to brute force a good password. – PiTheNumber Nov 28 '11 at 09:14
  • Apparently it doesn't work anyway. I thought about it more and took a look on how to calculate that. At the moment where I'd need the nonce I don't know the username. So I can't get the nonce from the stored user row. – Michael Weibel Nov 30 '11 at 07:26
  • Why don't you know the username? How does a login without username work? – PiTheNumber Nov 30 '11 at 08:49
  • It works like this (standard DIGEST-MD5 implementation of XMPP): User sends "I want to authenticate using DIGEST-MD5" - server sends a challenge with nonce, realm etc. User replies with username, some other infos and the calculated response value using the nonce, password etc. After that, the server has to check if the response matches with his own calculated hash (using the stored password) – Michael Weibel Nov 30 '11 at 09:25