Page 1 of 1

[Article Draft] Creating a Secure Login

Posted: Tue Oct 13, 2009 2:37 pm
by Chalks
EDIT: a much better tutorial can be found here


One of the more confusing things I’ve encountered in web development is creating a secure login system. I had trouble finding a clearly laid out guide that explained exactly what steps to take. So, now that I’m fairly familiar with the process, I’ve decided to write this guide as a practical series of steps using PHP, MySQL, and a bit of Javascript. Before you get started, you should realize that these steps are sufficient for anything as sensative as up to (and maybe including) a webmail system. Anything involving monetary transactions MUST take further steps which go beyond the scope of this article.

We need to define a few terms first:
Salt – A randomly generated string of at least 70 characters, unique for every user. This is stored in your database.
Pepper – Generated the same way as the salt, but only once, same for every user and is stored in a php file.
Hash – Any of several algorithms that encrypts data in such a way that it is almost impossible to reverse back to the original string. It is generally recommended that you use SHA256 or, in a few years or so, something better.
Username – A unique identifier for every user. For the purposes of this guide, we will assume that you force it to contain only Aa-Zz, 0-9 and underscores.
Password – A bit of information that may or may not be unique. It may contain any characters.

Unless mentioned otherwise, everything is done server side.
Now, the procedure I use for registering a new user:
  1. (client side) Concatenate the username and password together and hash them. Send this hash to the server.
  2. Generate a new salt, concatenate it with the result from 1 and the pepper. Hash this.
  3. Store the result from 2 and the salt in the database
Login procedure is almost the same:
  1. (client side) Concatenate the username and password together and hash them. Send this hash to the server.
  2. Retrieve old salt from database, concatenate it with the result from 1 and the pepper. Hash this.
  3. If the result from 2 matches with the old stored hash, then the user is valid, procede to 4. Otherwise, return an error message.
  4. Generate a new salt, concatenate it with the result from 1 and the pepper. Hash this.
  5. Store the result from 4 and the new salt in the database, overwriting the old values.
A few points to make about all this: First, step 1 is optional. I prefer to hash things on the client side initially so that I’m not sending plaintext passwords over the network. However, doing so means that in step 2 you are hashing a hash. This is technically not a good idea as it decreases the effectiveness of the hash. However, in my opinion, preventing the majority of your users (i.e. those who have Javascript enabled) from sending plaintext passwords over a potentially unsecure network is much more important.

Next, I always concatenate the username and password together because this ensures beyond any shadow of a doubt that the thing that is hashed is absolutely unique. Yeah, the random salt does this too, but… random is random and I like to make sure. As for the length of the salt (and pepper), having it longer is better. Basically, it forces the SHA256 algorithm to do more work which increases entropy and such.

Finally, steps 4 and 5 of the login were the steps which took me the most time to understand. The basic idea is that a salt is completely useless unless you can remember it. So, you store it alongside your hashed password (or, “keyphrase”… i.e. username + password + salt + pepper) and use it once before you ditch it and create a new one. This keeps your user table constantly changing, which is a great defence against any would be hacker.

Resources and code examples:
A great, concise article detailing why many of these steps are needed, written by devnetwork’s own Mordred.

Javascript SHA256 implementation

Hash(), usage: hash(“sha256”, “whatNeedsHashing”);

Creating a random string consisting of the characters 0-9, Aa-Zz, :, ;, <, =, >, ?, @, [, \, ], ^, _, `, {, |, }, or ~.

Code: Select all

for($i=0; $i<$length; $i++) {
  $salt .= chr((mt_rand() % 78) + 48);
}
Checking if a username consists of Aa-Zz, 0-9 or _

Code: Select all

If(preg_match(“#[^0-9a-z_]#i”, $username)
  return $badusername;

Final Thoughts:
When concatenating strings together, don't do it in the order I have listed above. Be creative! Maybe split your salt up in a predetermined manner while mixing it in with your username/password. The only reason to keep things simple in a thing like this is sheer laziness.

If you would like to see the entirety of one of my current login scripts, I have it available upon request. Just shoot me a pm.

These steps are by no means an exhaustive method to ensuring your user's security. If you really want it "perfect", you need to use an https connection as well as probably doing some sort of handshake method (as helpfully detailed by Maugrim).
------------------------------------------------------------


Please help me improve this article! Are there any glaring errors (especially factual ones)? In particular, is hashing the password client side truly helpful, or am I just pulling stuff out of thin air? Finally, what would you feel comfortable using such a system for? Also, hello everyone... I haven't posted anything in a long time, but I've been lurking!

Re: [Article Draft] Creating a Secure Login

Posted: Fri Oct 16, 2009 10:55 am
by Mordred
Thumbs up for the effort, it's something that's indeed needed, and is the basis of many of the questions here in the security forum.

Some comments:
1. Steps 4 and 5 from the login procedure are not needed and do not add to the security of the system. What are your motives exactly?
2. Hashing a hash is not a big deal (it's one of the major "bugs" in my article you link to). Also HMAC is a robust way of generating hash-based signatures, I'd use it.
3. With this protocol you would not be able to determine if usernames collide.
4. You may want to cover additional issues with changing password, recovering a forgotten password, protection against bruteforce.
5. Code, or it didn't happen ;)
---
devnetwork’s own Mordred.
6. Nobody owns me! Well, except my wife ... oh, and my bank ... oh, and my company ... oh, and an Italian guy I owe money to ... oh, and one of my ex-es that blackmails me ... oh, and ....
7. Welcome back ;) Sadly, I'm in lurk mode too.

Re: [Article Draft] Creating a Secure Login

Posted: Fri Oct 16, 2009 11:22 am
by Chalks
Mordred wrote:Thumbs up for the effort, it's something that's indeed needed, and is the basis of many of the questions here in the security forum.

Some comments:
1. Steps 4 and 5 from the login procedure are not needed and do not add to the security of the system. What are your motives exactly?
2. Hashing a hash is not a big deal (it's one of the major "bugs" in my article you link to). Also HMAC is a robust way of generating hash-based signatures, I'd use it.
3. With this protocol you would not be able to determine if usernames collide.
4. You may want to cover additional issues with changing password, recovering a forgotten password, protection against bruteforce.
5. Code, or it didn't happen ;)
Thanks!
1: 4 and 5 were added because I thought "huh, a table that constantly updates? How easy is that for a hacker to keep track of?!?!" Now that I think about it, it shouldn't really make a difference.

2: Will look into HMAC, I hadn't heard of it before.

3: I'm not sure what you mean. I'm assuming that most people check to make sure a username doesn't exist before they allow them to register.

4 & 5: Ok, ok. I was too lazy to add those in, next draft will have 'em. :D

Re: [Article Draft] Creating a Secure Login

Posted: Fri Oct 16, 2009 11:33 am
by superdezign

Code: Select all

User #1:
Username: foo
Password: bar
 
User #2:
Username: fo
Password: obar
Possible problem? You don't make this clear.

Re: [Article Draft] Creating a Secure Login

Posted: Fri Oct 16, 2009 11:44 am
by Eran
Possible problem? You don't make this clear.
It's not a problem since the salt makes the hash unique. In my opinion though, there is no real reason to concatenate the username with the password. Also, it seems to me there are too many steps:
Registration:
1. Create a salt, hash the password with the salt and pepper and store the result together in the same field, separated by a colon. Something like:
b65f13c7502a9a268b7d4d0a4a5bf137d65e9517772f2a5b19cc6e2ce994551d:4a93bf7
(hash:salt)

Login:
1. retrieve the row for the username, if exists, and separate the salt from the hash (using explode() or something similar)
2. Check if database hash matches the hash of the password + salt + pepper

Re: [Article Draft] Creating a Secure Login

Posted: Sat Oct 17, 2009 9:33 am
by kaisellgren
Just some thoughts:

- Salts and these "peppers", why did you write 70 characters? You aim at maximal strength with these, in which case you would use random bytes, not characters, of as many as possible. Salts ensure different hashes for same passwords of different users, so, basically there should be at least as many different combinations of salts as you have users, thus, with one million users, you should have at least a salt of about 20-bits of strength. Preferably more. They also make rainbow tables ineffective. I would suggest to use at least 24-bits of strength as for salts, but as much as the server can take, of course. And these values should be generated via a strong (P)RNG. Personally, I write software that are scalable and never limited to any specific salt lengths. As the time goes on, you can increase the size of the salt if the server admin prefers so.

- I have said this in other threads a couple of times I guess, but Whirlpool is a hash worth mentioning. It has its strengths.

- Usernames and passwords should be as random as possible, as strong as possible and as secret as possible. They shouldn't be stored anywhere except for one place where they are being compared to during logon process. Limiting them to a-z is not good.

Those are some things you could think about. I didn't read the rest of your thread as my time is a kind of limited. Anyway, good luck with your research.

Re: [Article Draft] Creating a Secure Login

Posted: Sat Oct 17, 2009 12:36 pm
by superdezign
kaisellgren wrote:- Usernames and passwords should be as random as possible, as strong as possible and as secret as possible. They shouldn't be stored anywhere except for one place where they are being compared to during logon process. Limiting them to a-z is not good.
Most websites use usernames as a way for other users to identify each other, so allowing other characters would limit you (i.e. using the username in a user's profile URL).

Re: [Article Draft] Creating a Secure Login

Posted: Sat Oct 17, 2009 12:47 pm
by arjan.top
you can url encode it

Re: [Article Draft] Creating a Secure Login

Posted: Sat Oct 17, 2009 12:56 pm
by superdezign
arjan.top wrote:you can url encode it
I'm not sure, but I think that URL encoding is limited to a set of characters that UNICODE is not limited to.

Re: [Article Draft] Creating a Secure Login

Posted: Sat Oct 17, 2009 3:02 pm
by kaisellgren
Then there should be a nick name, or anything similar.

Re: [Article Draft] Creating a Secure Login

Posted: Sun Oct 18, 2009 4:06 am
by arjan.top
superdezign wrote: I'm not sure, but I think that URL encoding is limited to a set of characters that UNICODE is not limited to.
The generic URI syntax mandates that new URI schemes that provide for the representation of character data in a URI must, in effect, represent characters from the unreserved set without translation, and should convert all other characters to bytes according to UTF-8, and then percent-encode those values. This requirement was introduced in January 2005 with the publication of RFC 3986. URI schemes introduced before this date are not affected.
http://en.wikipedia.org/wiki/Url_encode