Absolutely 100% safe login script?
Moderator: General Moderators
Absolutely 100% safe login script?
I need to write a login / user authentication script that's absolutely safe. Of course 100% is never possible, but I want to make sure not to have any known security holes.
My considerations so far:
- https only, http connections are refused or redirected to https
- the obvious anti injection measures, like escaping all strings in queries that come from user input or can possible be modified by users (like urls, cookies, post vars, session id's, etc)
- don't store passwords in database, only hashes of salt+peppered passwords (so no rainbow tables can be used and similar passwords cannot be detected)
- don't submit password from login form directly, but hash it with some time-limited random token (so somebody else submitted the same data later on will not be able to login, even if there's an https spoofing man in the middle)
- store some session flag upon successful login, which is checked in every page of the secured area, if it's missing, redirect to login
Am I on the right track?
I am breaking my head on the hashing / salting / random token part. How can I hash the password with some time dependent token on one end (login form), and with a salt+pepper on the other end (database), and still compare them?
About which kind of hash to use: with the recent progress on breaking MD5 and SHA1, I assume SHA512 is the best choice?
My considerations so far:
- https only, http connections are refused or redirected to https
- the obvious anti injection measures, like escaping all strings in queries that come from user input or can possible be modified by users (like urls, cookies, post vars, session id's, etc)
- don't store passwords in database, only hashes of salt+peppered passwords (so no rainbow tables can be used and similar passwords cannot be detected)
- don't submit password from login form directly, but hash it with some time-limited random token (so somebody else submitted the same data later on will not be able to login, even if there's an https spoofing man in the middle)
- store some session flag upon successful login, which is checked in every page of the secured area, if it's missing, redirect to login
Am I on the right track?
I am breaking my head on the hashing / salting / random token part. How can I hash the password with some time dependent token on one end (login form), and with a salt+pepper on the other end (database), and still compare them?
About which kind of hash to use: with the recent progress on breaking MD5 and SHA1, I assume SHA512 is the best choice?
- jayshields
- DevNet Resident
- Posts: 1912
- Joined: Mon Aug 22, 2005 12:11 pm
- Location: Leeds/Manchester, England
Re: Absolutely 100% safe login script?
You list most of the important points to a login script so I think your best bet is to have a go and post what you achieve - maybe even a live sandbox. More people will get involved and give you pointers then.
Re: Absolutely 100% safe login script?
I have some difficulties that prevent me from beginning...
For example, the passwords in the database are stored like hash(password+A) where A is the salt. If I want the login script to also hash the entered password before transmitting it (I guess with JavaScript), it means I *must* use salt A there as well, otherwise I will never be able to compare them. Isn't it a security breach if my salt data A is publicly visible at the client side?
And what about the pepper (different salt for each user). I can not know in advance which pepper to use at client side, because I don't know which user is going to log in. I can hash in two steps: in the database I store hash(hash(password+salt)+pepper) (and also store the pepper string separately), and the form sends hash(password+salt), then then how do I verify the password? I cannot do "select * from users where SHA512(submitted_password+pepper)=stored_password" because MySQL does not support SHA512..?
The the only way I can think of is selecting *all* password hashes from the database. And do the SHA512 hashing + comparing them all in PHP. But that seems stupid, and besides isn't that a security risk too (especially when the database connection is not local)?
For example, the passwords in the database are stored like hash(password+A) where A is the salt. If I want the login script to also hash the entered password before transmitting it (I guess with JavaScript), it means I *must* use salt A there as well, otherwise I will never be able to compare them. Isn't it a security breach if my salt data A is publicly visible at the client side?
And what about the pepper (different salt for each user). I can not know in advance which pepper to use at client side, because I don't know which user is going to log in. I can hash in two steps: in the database I store hash(hash(password+salt)+pepper) (and also store the pepper string separately), and the form sends hash(password+salt), then then how do I verify the password? I cannot do "select * from users where SHA512(submitted_password+pepper)=stored_password" because MySQL does not support SHA512..?
The the only way I can think of is selecting *all* password hashes from the database. And do the SHA512 hashing + comparing them all in PHP. But that seems stupid, and besides isn't that a security risk too (especially when the database connection is not local)?
- jayshields
- DevNet Resident
- Posts: 1912
- Joined: Mon Aug 22, 2005 12:11 pm
- Location: Leeds/Manchester, England
Re: Absolutely 100% safe login script?
You're overcomplicating things.
- Don't do anything with passwords prior to transmission (at the client side) - HTTPS will encrypt this.
- PHP can hash things using SHA.
- For peppers you can use any arbitary user data such as date joined.
I can see what you're saying about MySQLs inability to natively SHA512 stuff - if that's actually the case, and the connection to your external MySQL server is not encrypted, then you'll have to fetch all hashed passwords and iterate them in PHP for retrieval, and do the hashing in PHP before the transmission - this way nothing is sent in plain text.
- Don't do anything with passwords prior to transmission (at the client side) - HTTPS will encrypt this.
- PHP can hash things using SHA.
- For peppers you can use any arbitary user data such as date joined.
I can see what you're saying about MySQLs inability to natively SHA512 stuff - if that's actually the case, and the connection to your external MySQL server is not encrypted, then you'll have to fetch all hashed passwords and iterate them in PHP for retrieval, and do the hashing in PHP before the transmission - this way nothing is sent in plain text.
- kaisellgren
- DevNet Resident
- Posts: 1675
- Joined: Sat Jan 07, 2006 5:52 am
- Location: Lahti, Finland.
Re: Absolutely 100% safe login script?
It is good to see you have taken security seriously. The approaches you are going to take (or you have taken) are good. However, this
If you want to have a salted challenge-response authentication, read more: http://tools.ietf.org/html/draft-newman-auth-scram-07
Is useless, because you are operating under HTTPS. Just send the credentials as is since the whole request will be encrypted anyway.GimbaL wrote:- don't submit password from login form directly, but hash it with some time-limited random token (so somebody else submitted the same data later on will not be able to login, even if there's an https spoofing man in the middle)
Read above, you do not need to.GimbaL wrote:I am breaking my head on the hashing / salting / random token part. How can I hash the password with some time dependent token on one end (login form), and with a salt+pepper on the other end (database), and still compare them?
Being "the best" is a point of view. Fastest? Strongest? Most secure? If you use SHA-512, do not worry about its security (at least for a long time I hope).GimbaL wrote:About which kind of hash to use: with the recent progress on breaking MD5 and SHA1, I assume SHA512 is the best choice?
If you want to have a salted challenge-response authentication, read more: http://tools.ietf.org/html/draft-newman-auth-scram-07
Re: Absolutely 100% safe login script?
Thanks for your replies.
Since you suggest not to do any hashing client side (just send the raw password over https) it means I must do all the hashing, salting and challenging in PHP. How can I still get a randomized challenge? (or do I totally misunderstand the idea?)
jayshields wrote:- Don't do anything with passwords prior to transmission (at the client side) - HTTPS will encrypt this.
What about a possible man-in-the-middle attack? The recent SSL certificate checksums being forged, especially in combination with a possible spoofed DNS, makes me a bit paranoia.kaisellgren wrote:Is useless, because you are operating under HTTPS. Just send the credentials as is since the whole request will be encrypted anyway.
In this case that would be strongest / most secure (what's the difference?) so I guess SHA512 is best for me.kaisellgren wrote:Being "the best" is a point of view. Fastest? Strongest? Most secure? If you use SHA-512, do not worry about its security (at least for a long time I hope).
Thanks... Not to be lazy, but is it possible to explain the idea behind this in a few lines? This document looks very formal, and I'm not sure if I get the big picture.If you want to have a salted challenge-response authentication, read more: http://tools.ietf.org/html/draft-newman-auth-scram-07
Since you suggest not to do any hashing client side (just send the raw password over https) it means I must do all the hashing, salting and challenging in PHP. How can I still get a randomized challenge? (or do I totally misunderstand the idea?)
- kaisellgren
- DevNet Resident
- Posts: 1675
- Joined: Sat Jan 07, 2006 5:52 am
- Location: Lahti, Finland.
Re: Absolutely 100% safe login script?
That is something you can stop worrying about.GimbaL wrote:What about a possible man-in-the-middle attack? The recent SSL certificate checksums being forged, especially in combination with a possible spoofed DNS, makes me a bit paranoia.
I suggest you to use either SHA-256, SHA-512 or Whirlpool. The first produces a 256-bit output and the rest produce 512-bit output. Yes, SHA-512 is a good choice.GimbaL wrote:In this case that would be strongest / most secure (what's the difference?) so I guess SHA512 is best for me.
It requires that you expose your salt. That is not a problem as long as the hash value itself is safe, but like I said earlier, if you use HTTPS for your site, you do not need this.GimbaL wrote:Thanks... Not to be lazy, but is it possible to explain the idea behind this in a few lines? This document looks very formal, and I'm not sure if I get the big picture.
Most applications do hashing on the server-side. I am not sure what you are referring to with that "challenging in PHP". If you use a secret key on the filesystem that you add into the hash then you must hash on the server-side (and you cannot use Challenge-Response).GimbaL wrote:Since you suggest not to do any hashing client side (just send the raw password over https) it means I must do all the hashing, salting and challenging in PHP. How can I still get a randomized challenge? (or do I totally misunderstand the idea?)
-
leonel.machava
- Forum Newbie
- Posts: 10
- Joined: Fri May 15, 2009 4:28 pm
Re: Absolutely 100% safe login script?
Hi GimbaL.
Hi Kai (congratulations for your Triton CMS project).
I am glad to know you are conscious about security.
I am developing a PHP framework/CMS and I implement registration/authentication in this way:
Registration process:
(1) The client sends username, password, etc, via HTTPS.
(2) I generate a random salt and I use it to hash the client password. This is the code:
(3) Store all client data (name, hashed password, etc) in a database.
Login process:
(1) The client sends credentials via HTTPS.
(2) I search a user with the supplied name in the database. If the user exists I get its stored hashed password. I use the code given bellow to check if the client password is correct.
Some notes:
a) When you send data via HTTPS you don't have to worry about a man-in-the-middle or replay attack. TLS/SSL does client/server authentication and encryption. It means that only your server will get and understand the user submitted data.
b) You don't need to do anything on the client-side. The browser does the work for you (all the TLS/SSL things).
c) I generate a random salt with this algorithm:. The salt is composed of 32 hex chars.
d) I used the MD5 algorithm but I recommend a stronger algorithm like SHA512. For using SHA512 with PHP, see http://www.php.net/manual/en/function.hash.php .
e) Even if an hacker gets access to the hashed passwords it is very (very) difficult to him to decrypt the password with brute force. Even knowing that the first 32 characters are the salt.
Besides a strong authentication, beware of:
Cheers,
Leonel Machava
Hi Kai (congratulations for your Triton CMS project).
I am glad to know you are conscious about security.
I am developing a PHP framework/CMS and I implement registration/authentication in this way:
Registration process:
(1) The client sends username, password, etc, via HTTPS.
(2) I generate a random salt and I use it to hash the client password. This is the code:
Code: Select all
function hash_password($clear_pass,$salt = NULL) {
if( !isset($salt) ) {
$salt = md5(uniqid(rand(),TRUE));
}
return $salt . md5($clear_pass);
}Login process:
(1) The client sends credentials via HTTPS.
(2) I search a user with the supplied name in the database. If the user exists I get its stored hashed password. I use the code given bellow to check if the client password is correct.
Code: Select all
// This function checks if a clean pass corresponds to an hashed pass.
function check_pass($clean_pass,$hashed_pass) {
// Get the salt from the hashed pass (first 32 character).
$salt = substr($hashed_pass,32);
$test_hash = hash_password($clean_pass,$salt);
return $test_hash == $hashed_pass;
}
a) When you send data via HTTPS you don't have to worry about a man-in-the-middle or replay attack. TLS/SSL does client/server authentication and encryption. It means that only your server will get and understand the user submitted data.
b) You don't need to do anything on the client-side. The browser does the work for you (all the TLS/SSL things).
c) I generate a random salt with this algorithm:
Code: Select all
$salt = md5(uniqid(rand(),TRUE);d) I used the MD5 algorithm but I recommend a stronger algorithm like SHA512. For using SHA512 with PHP, see http://www.php.net/manual/en/function.hash.php .
e) Even if an hacker gets access to the hashed passwords it is very (very) difficult to him to decrypt the password with brute force. Even knowing that the first 32 characters are the salt.
Besides a strong authentication, beware of:
- SQL Injection
- Cross Site Scripting (XSS)
- Cross Site Request Forgery
- Session hijacking
- And others
Cheers,
Leonel Machava
Last edited by Benjamin on Sat May 16, 2009 9:58 am, edited 2 times in total.
Reason: Changed code type from text to php.
Reason: Changed code type from text to php.
- kaisellgren
- DevNet Resident
- Posts: 1675
- Joined: Sat Jan 07, 2006 5:52 am
- Location: Lahti, Finland.
Re: Absolutely 100% safe login script?
Eh, well, thanks, but it's almost 2 years old and I made it when I was starting with PHP...leonel.machava wrote:Hi Kai (congratulations for your Triton CMS project).
The way you generate salt is weak and so is your hash algorithm.leonel.machava wrote:(2) I generate a random salt and I use it to hash the client password. This is the code:Code: Select all
function hash_password($clear_pass,$salt = NULL) { if( !isset($salt) ) { $salt = md5(uniqid(rand(),TRUE)); } return $salt . md5($clear_pass); }
Also, just a thought: wouldn't it be better to store the salt in a separate column? This way you are not wasting the column space for Salt+Hash, but for each separately. I mean, if you use VARCHAR, for instance, for your hash value column, then you may not be able to use more than 255 bytes for the column and your salt would eat 32 bytes itself already (if not more in the future).
Not exactly true. Anyone else in between the server and the client or even "near" these nodes (eavesdropper) can get the user and the server submitted data. However, the data is encrypted and therefore useless.leonel.machava wrote:It means that only your server will get and understand the user submitted data.
Actually, if you are hashing your salt then SHA-512 is not likely to be any better than MD5 and in fact, you could just drop off that hashing from your salts. In case of password hashing, SHA-512 would be way more secure choice.leonel.machava wrote:d) I used the MD5 algorithm but I recommend a stronger algorithm like SHA512. For using SHA512 with PHP, see http://www.php.net/manual/en/function.hash.php .
The salt prevents attacks, which utilize rainbow tables since the use of a 32-byte salt equals to Very large precalculated table. Salting does not help in case of brute forcing or dictionary attacks as the salt is known by the attacker.leonel.machava wrote:e) Even if an hacker gets access to the hashed passwords it is very (very) difficult to him to decrypt the password with brute force. Even knowing that the first 32 characters are the salt.
The Othersleonel.machava wrote:Besides a strong authentication, beware of:
And others