www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Password Storage

reply brian <brian infinityplusb.com> writes:
I'm starting to build a small web-based application where I would 
like to authenticate users, and hence need to store passwords.

After reading this:
http://blog.codinghorror.com/youre-probably-storing-passwords-incorrectly/
and many other posts that I zombie-surfed to from that page, I'm 
now fearful of doing this badly. :(

My reading of that post was that I should be storing things as:

hash = md5('salty-' + password)

So when a user tries to authenticate, I need to:
1) validate the user id
2) find the unique "salt" I generated for that user when they 
registered
3) pre- or post-pend the salt to the password entered (apparently 
there is a difference??)
4) md5 the lot
5) check this md5(salt+password) against what I have stored.

So for each user, I need to store in my database:
UserName/UserID
Salt
Hashed_Password

Can the developers in the room confirm if this is the correct 
approach?
Are there examples of betters ways of doing this?

Regards
Brian
Nov 26 2015
next sibling parent reply Alex Parrill <initrd.gz gmail.com> writes:
On Friday, 27 November 2015 at 00:17:34 UTC, brian wrote:
 I'm starting to build a small web-based application where I 
 would like to authenticate users, and hence need to store 
 passwords.

 After reading this:
 http://blog.codinghorror.com/youre-probably-storing-passwords-incorrectly/
 and many other posts that I zombie-surfed to from that page, 
 I'm now fearful of doing this badly. :(

 My reading of that post was that I should be storing things as:

 hash = md5('salty-' + password)

 So when a user tries to authenticate, I need to:
 1) validate the user id
 2) find the unique "salt" I generated for that user when they 
 registered
 3) pre- or post-pend the salt to the password entered 
 (apparently there is a difference??)
 4) md5 the lot
 5) check this md5(salt+password) against what I have stored.

 So for each user, I need to store in my database:
 UserName/UserID
 Salt
 Hashed_Password

 Can the developers in the room confirm if this is the correct 
 approach?
 Are there examples of betters ways of doing this?

 Regards
 Brian
Do not use MD5 or SHA for hashing passwords. Use PBKDF2, bcrypt, or maybe scrypt. There should be C libraries available for those algorithms; use them. More info: http://security.stackexchange.com/questions/211/how-to-securely-hash-passwords/31846#31846
Nov 26 2015
parent reply brian <brian infinityplusb.com> writes:
On Friday, 27 November 2015 at 00:42:09 UTC, Alex Parrill wrote:
 On Friday, 27 November 2015 at 00:17:34 UTC, brian wrote:
 I'm starting to build a small web-based application where I 
 would like to authenticate users, and hence need to store 
 passwords.

 After reading this:
 http://blog.codinghorror.com/youre-probably-storing-passwords-incorrectly/
 and many other posts that I zombie-surfed to from that page, 
 I'm now fearful of doing this badly. :(

 My reading of that post was that I should be storing things as:

 hash = md5('salty-' + password)

 So when a user tries to authenticate, I need to:
 1) validate the user id
 2) find the unique "salt" I generated for that user when they 
 registered
 3) pre- or post-pend the salt to the password entered 
 (apparently there is a difference??)
 4) md5 the lot
 5) check this md5(salt+password) against what I have stored.

 So for each user, I need to store in my database:
 UserName/UserID
 Salt
 Hashed_Password

 Can the developers in the room confirm if this is the correct 
 approach?
 Are there examples of betters ways of doing this?

 Regards
 Brian
Do not use MD5 or SHA for hashing passwords. Use PBKDF2, bcrypt, or maybe scrypt. There should be C libraries available for those algorithms; use them. More info: http://security.stackexchange.com/questions/211/how-to-securely-hash-passwords/31846#31846
Thanks for the blatant faux pas. I wasn't going to use MD5, I just meant "hash it somehow", which was not apparent from my question. My bad. Algorithm aside, the rest of that approach seems sensible then? The hash implementation was probably going to be a part 2 of this question. I'd use dcrypt (https://github.com/puzzlehawk/dcrypt) to keep all the d-goodness, but according to the author, that's not "production ready" yet. In lieu of that, I'll have a gander at those libraries you mentioned.
Nov 26 2015
parent Alex Parrill <initrd.gz gmail.com> writes:
On Friday, 27 November 2015 at 00:50:25 UTC, brian wrote:
 Thanks for the blatant faux pas.
 I wasn't going to use MD5, I just meant "hash it somehow", 
 which was not apparent from my question. My bad.

 Algorithm aside, the rest of that approach seems sensible then?

 The hash implementation was probably going to be a part 2 of 
 this question.
 I'd use dcrypt (https://github.com/puzzlehawk/dcrypt) to keep 
 all the d-goodness, but according to the author, that's not 
 "production ready" yet.
 In lieu of that, I'll have a gander at those libraries you 
 mentioned.
Yea. I've used bcrypt a few times; it's usually just using the hash function to hash the passwords, then the check function to check them, and that's it (bcrypt stores the salt along with the password). I don't know if I'd trust dcrypt yet. No offence to the authors, but I doubt that it has gone through the review that more popular C libraries have.
Nov 26 2015
prev sibling next sibling parent reply "H. S. Teoh via Digitalmars-d-learn" <digitalmars-d-learn puremagic.com> writes:
On Fri, Nov 27, 2015 at 12:17:32AM +0000, brian via Digitalmars-d-learn wrote:
 I'm starting to build a small web-based application where I would like
 to authenticate users, and hence need to store passwords.
 
 After reading this:
 http://blog.codinghorror.com/youre-probably-storing-passwords-incorrectly/
 and many other posts that I zombie-surfed to from that page, I'm now
 fearful of doing this badly. :(
 
 My reading of that post was that I should be storing things as:
 
 hash = md5('salty-' + password)
 
 So when a user tries to authenticate, I need to:
 1) validate the user id
 2) find the unique "salt" I generated for that user when they registered
 3) pre- or post-pend the salt to the password entered (apparently there is a
 difference??)
 4) md5 the lot
 5) check this md5(salt+password) against what I have stored.
 
 So for each user, I need to store in my database:
 UserName/UserID
 Salt
 Hashed_Password
 
 Can the developers in the room confirm if this is the correct approach?
 Are there examples of betters ways of doing this?
[...] For authentication, the password shouldn't even be sent over the wire. Instead, the server (which knows the correct password) should send a challenge to the client (i.e., a large random number produced by a good RNG -- which is different each time the user authenticates). The client should then prepend this challenge to the password typed in by the user, and compute the hash of the result. This hash is sent back to the server, which does the same computation on its own, and checks whether the two hash values match. Provided you're using a good cryptographic hash, the only way the client will be able to provide the right answer is if the user actually knows the password. At no time is the password ever sent over the network, encrypted or not. --T
Nov 26 2015
next sibling parent reply brian <brian infinityplusb.com> writes:
On Friday, 27 November 2015 at 02:05:49 UTC, H. S. Teoh wrote:
...
 At no time is the password ever sent over the network, 
 encrypted or not.

 --T
So, I understand what you are trying to say, but I'm stuck on the specifics of implementation, if you'll bear with me.
 For authentication, the password shouldn't even be sent over 
 the wire. Instead, the server (which knows the correct 
 password) should send a challenge to the client
So my app is web based, so I don't really have a "client-server" model you are suggesting. I'm building it using Vibe.d with a mongodb backend, so hopefully the "client" will be a web-browser (or in future iterations, a mobile device - let's ignore that for now).
 random number produced by a good RNG -- which is different each 
 time the user authenticates)
I'm not sure why I need this, so I'm going to break down and example. Bob comes in with password "Password01" Once he enters "Password01" I want to: Add a string to it: "StaticRandomString~Password01" Then hash it: hash("StaticRandomString~Password01") which gives me "I#$%am%^&Random(*&LOL*&" Then to verify Bob is Bob I need to verify "I#$%am%^&Random(*&LOL*&" against something in the database? So in my DB I need to store : "I#$%am%^&Random(*&LOL*&" If *this* is the scenario, then the "StaticRandomString" needs to be the same all the time, so I need to store that in the DB too, no? So now my DB contains: "StaticRandomString" "I#$%am%^&Random(*&LOL*&" Your solution was to random generate the random string at verification time. If I do that I have: "RunTimeRandomString~Password01" Then hash that to get "I#$%Too$%456^(am(*$&Random(*&LOL*&" However I can't store that in the DB, because the "RunTimeRandomString" which will produce a different hashed value. Sooo, I need to change this scenario to: Get the Password from the client/user and hash it. Then add on the randomness: "RunTimeRandomString~hashed(clientEntered-Password01)" Get that answer back. Get the password from the server/database and hash it. Add on the same randomness. "RunTimeRandomString~hashed(actualPassword-Password01)" Thus in my db I only need to stored hashed(Password01) Compare results. ... Profit. Am I correct in these descriptions? Which is better? I know this is pedantic and not very language specific, but this is the crux of what I want to know. Doing it is easy. The "making sure I'm doing it right" bit is hard...
Nov 26 2015
parent reply "H. S. Teoh via Digitalmars-d-learn" <digitalmars-d-learn puremagic.com> writes:
On Fri, Nov 27, 2015 at 03:09:38AM +0000, brian via Digitalmars-d-learn wrote:
 On Friday, 27 November 2015 at 02:05:49 UTC, H. S. Teoh wrote:
 ...
At no time is the password ever sent over the network, encrypted or not.

--T
So, I understand what you are trying to say, but I'm stuck on the specifics of implementation, if you'll bear with me.
For authentication, the password shouldn't even be sent over the
wire.  Instead, the server (which knows the correct password) should
send a challenge to the client
So my app is web based, so I don't really have a "client-server" model you are suggesting. I'm building it using Vibe.d with a mongodb backend, so hopefully the "client" will be a web-browser (or in future iterations, a mobile device - let's ignore that for now).
In this case, the "client" would be the web browser. I'm not too familiar with what a web browser might provide javascript on the page, but if javascript has a standard hashing function that could be used for this purpose.
random number produced by a good RNG -- which is different each time
the user authenticates)
I'm not sure why I need this, so I'm going to break down and example.
[...] Based on others' reply, maybe the approach I'm suggesting may not be the best implementation for your case, but in any case, here is how it would work: 1) The server stores password01 in the user database. 2) A client (browser) connects to the server and claims to be a valid user. 3) The server generates a random number, let's call it X, and sends X to the client. (X is the "challenge".) This is done each time somebody tries to authenticate with the server (the value of X will be different each time). 4) The client receives X, prepends it to the password that user types in (presumably the same as password01). The client then computes hash(X + pasword) and sends the result, let's call it Y, back to the server. 5) Meanwhile, the server also computes hash(X + password01), and obtains a value Z. 6) The server receives Y from the client, and compares Y with Z. If Y==Z, then it proves that the client knows the correct password, even though the password itself is never transmitted over the network, because the only way the client can know the value of Z is if it knows the correct password (the user entered the correct password) and does the same computation as the server. If Y!=Z, then the password is incorrect and the server rejects the login attempt. The reason for step (3) is to prevent replay attacks: if the challenge is always the same, then a man-in-the-middle attacker can capture the packets between server and client, and replay the packets containing the client's response to the server later, thus obtaining access to user's account. Since the server's challenge is a random number that's different every time, the attacker won't be able to provide the correct response by replaying a previous correct answer. The reason for step (4) is to prevent an eavesdropper from recovering the password by man-in-the-middle attacks. If the password is sent in plaintext, an attacker that compromised a router between the client and the server (or runs a transparent proxy masquerading as the real server) would be able to read the password off the packet while it's in transit. Even if the password is sent in encrypted form, an attacker who obtains a copy of the ciphertext could run brute-force attacks to recover the plaintext password. By only transmitting a hash (presumably a 1-way hash) back to the server, even if an attacker somehow manages to get a hold of the hash value, it won't actually reveal the password. T -- The irony is that Bill Gates claims to be making a stable operating system and Linus Torvalds claims to be trying to take over the world. -- Anonymous
Nov 26 2015
parent reply Adam D. Ruppe <destructionator gmail.com> writes:
On Friday, 27 November 2015 at 07:46:33 UTC, H. S. Teoh wrote:
 1) The server stores password01 in the user database.
I still wouldn't actually store this, hash it anyway and use that as the new "password".
Nov 27 2015
parent reply "H. S. Teoh via Digitalmars-d-learn" <digitalmars-d-learn puremagic.com> writes:
On Fri, Nov 27, 2015 at 02:51:30PM +0000, Adam D. Ruppe via Digitalmars-d-learn
wrote:
 On Friday, 27 November 2015 at 07:46:33 UTC, H. S. Teoh wrote:
1) The server stores password01 in the user database.
I still wouldn't actually store this, hash it anyway and use that as the new "password".
True, so you'd store hash(password01) in the database, and compute hash(X + hash(password)) during authentication. T -- It is of the new things that men tire --- of fashions and proposals and improvements and change. It is the old things that startle and intoxicate. It is the old things that are young. -- G.K. Chesterton
Nov 27 2015
next sibling parent BLM768 <blm768 gmail.com> writes:
On Friday, 27 November 2015 at 16:14:06 UTC, H. S. Teoh wrote:
 True, so you'd store hash(password01) in the database, and 
 compute
 hash(X + hash(password)) during authentication.


 T
Another option is SCRAM: https://en.wikipedia.org/wiki/Salted_Challenge_Response_Authentication_Mechanism
Nov 27 2015
prev sibling parent Chris Wright <dhasenan gmail.com> writes:
On Fri, 27 Nov 2015 08:09:49 -0800, H. S. Teoh via Digitalmars-d-learn
wrote:

 On Fri, Nov 27, 2015 at 02:51:30PM +0000, Adam D. Ruppe via
 Digitalmars-d-learn wrote:
 On Friday, 27 November 2015 at 07:46:33 UTC, H. S. Teoh wrote:
1) The server stores password01 in the user database.
I still wouldn't actually store this, hash it anyway and use that as the new "password".
True, so you'd store hash(password01) in the database, and compute hash(X + hash(password)) during authentication. T
Alternatively, you could do what's industry standard for non-sensitive sites: * Always use TLS. * Send passwords to the server. Memory regurgitation attacks can reveal these passwords, which allows attackers to reuse those credentials on other sites to impersonate people. * Store salted hashes of passwords rather than the passwords themselves. * Hope that your certificate authority is secure. Actually, hope that *every* certificate authority is secure. Otherwise, MITM attack. Or you can take a slightly more paranoid approach: * Use certificate pinning to prevent MITM attacks. * The client sends a salted hash of their password. You store a salted hash of that. This saves users from their password reuse habits if someone finds an arbitrary code execution flaw or memory regurgitation attack in your server. This approach is at least marginally more secure than yours (if your server randomly generates the same nonce twice for the same person, I can use a replay attack, for instance) and is better vetted. Security is hard.
Nov 27 2015
prev sibling next sibling parent BLM768 <blm768 gmail.com> writes:
On Friday, 27 November 2015 at 02:05:49 UTC, H. S. Teoh wrote:
 For authentication, the password shouldn't even be sent over 
 the wire. Instead, the server (which knows the correct 
 password) should send a challenge to the client (i.e., a large 
 random number produced by a good RNG -- which is different each 
 time the user authenticates). The client should then prepend 
 this challenge to the password typed in by the user, and 
 compute the hash of the result. This hash is sent back to the 
 server, which does the same computation on its own, and checks 
 whether the two hash values match. Provided you're using a good 
 cryptographic hash, the only way the client will be able to 
 provide the right answer is if the user actually knows the 
 password. At no time is the password ever sent over the 
 network, encrypted or not.


 --T
The issue I see with this is that the server has to _know_ the password in order to hash it with the challenge. If the server is compromised, guess who else knows the password now? Some kind of public-key encryption/signing might work, though.
Nov 26 2015
prev sibling parent Adam D. Ruppe <destructionator gmail.com> writes:
On Friday, 27 November 2015 at 02:05:49 UTC, H. S. Teoh wrote:
 For authentication, the password shouldn't even be sent over 
 the wire. Instead, the server (which knows the correct 
 password) should send a challenge to the client
Most web setups can't rely on that tho cuz of the lameness of client side scripting... But at least if the password is sent over https you don't have to worry too much about the wire.
Nov 26 2015
prev sibling next sibling parent Brad Anderson <eco gnuk.net> writes:
On Friday, 27 November 2015 at 00:17:34 UTC, brian wrote:
 [snip]

 Can the developers in the room confirm if this is the correct 
 approach?
 Are there examples of betters ways of doing this?

 Regards
 Brian
Botan has well thought out password hashing: https://github.com/etcimon/botan/wiki/Password-Hashing
Nov 27 2015
prev sibling next sibling parent Kagamin <spam here.lot> writes:
Another option: 
http://forum.dlang.org/post/lts93o$2fr0$1 digitalmars.com
Nov 27 2015
prev sibling parent sarn <sarn theartofmachinery.com> writes:
On Friday, 27 November 2015 at 00:17:34 UTC, brian wrote:
 3) pre- or post-pend the salt to the password entered 
 (apparently there is a difference??)
Sorry to revive an old thread, but I wrote a blog post about this question: https://theartofmachinery.com/2016/01/03/What%20Difference%20Can%20Order%20Make%20When%20Hashing.html
Jan 03