[Article Draft] Creating a Secure Login

Discussions of secure PHP coding. Security in software is important, so don't be afraid to ask. And when answering: be anal. Nitpick. No security vulnerability is too small.

Moderator: General Moderators

Post Reply
User avatar
Chalks
Forum Contributor
Posts: 447
Joined: Thu Jul 12, 2007 7:55 am
Location: Indiana

[Article Draft] Creating a Secure Login

Post 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!
Last edited by Chalks on Mon May 10, 2010 8:41 pm, edited 1 time in total.
User avatar
Mordred
DevNet Resident
Posts: 1579
Joined: Sun Sep 03, 2006 5:19 am
Location: Sofia, Bulgaria

Re: [Article Draft] Creating a Secure Login

Post 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.
User avatar
Chalks
Forum Contributor
Posts: 447
Joined: Thu Jul 12, 2007 7:55 am
Location: Indiana

Re: [Article Draft] Creating a Secure Login

Post 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
User avatar
superdezign
DevNet Master
Posts: 4135
Joined: Sat Jan 20, 2007 11:06 pm

Re: [Article Draft] Creating a Secure Login

Post 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.
User avatar
Eran
DevNet Master
Posts: 3549
Joined: Fri Jan 18, 2008 12:36 am
Location: Israel, ME

Re: [Article Draft] Creating a Secure Login

Post 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
User avatar
kaisellgren
DevNet Resident
Posts: 1675
Joined: Sat Jan 07, 2006 5:52 am
Location: Lahti, Finland.

Re: [Article Draft] Creating a Secure Login

Post 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.
User avatar
superdezign
DevNet Master
Posts: 4135
Joined: Sat Jan 20, 2007 11:06 pm

Re: [Article Draft] Creating a Secure Login

Post 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).
User avatar
arjan.top
Forum Contributor
Posts: 305
Joined: Sun Oct 14, 2007 4:36 am
Location: Hoče, Slovenia

Re: [Article Draft] Creating a Secure Login

Post by arjan.top »

you can url encode it
User avatar
superdezign
DevNet Master
Posts: 4135
Joined: Sat Jan 20, 2007 11:06 pm

Re: [Article Draft] Creating a Secure Login

Post 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.
User avatar
kaisellgren
DevNet Resident
Posts: 1675
Joined: Sat Jan 07, 2006 5:52 am
Location: Lahti, Finland.

Re: [Article Draft] Creating a Secure Login

Post by kaisellgren »

Then there should be a nick name, or anything similar.
User avatar
arjan.top
Forum Contributor
Posts: 305
Joined: Sun Oct 14, 2007 4:36 am
Location: Hoče, Slovenia

Re: [Article Draft] Creating a Secure Login

Post 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
Post Reply