More Secure User Authentication System
Posted 4 months, 1 week ago at 8:41 pm. 2 comments
UPDATE: The registration tutorial that goes along with this can be found here.
I wrote the other tutorial for a PHP login system as an example of how easy it is to make one, however It wasn’t exactly the most secure login system ever. So I have decided to re-visit the subject and make a better version. It is essentially the same but the hashing system for the password is now more secure due to the introduction of ’salting’.
Adding Some Salt To Those Chips
Ok, so my sense of humor stinks but I am going to show you how to make that simple login system a little more secure by adding a bit of ’salt’ to the passwords. I have also re-written the code to be a lot more verbose and friendlier for the end user. Let’s get started.
First let’s start with the page your form will be on. I am calling it auth.php in this example. You will need a form with a text field, a password field and a submit button. The information should be sent by post and it should point to our PHP script which I am calling login.php. You should also add this code to the top of you auth.php page:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | <?php switch($_GET['e']) { case "um" : echo '<div class="fail">Please enter a Username before submitting.</div>'; break; case "pm" : echo '<div class="fail">Please enter a Password before submitting</div>'; break; case "upm" : echo '<div class="fail">Please enter a Username & a Password before submitting</div>'; break; case "uw" : echo '<div class="fail">That Username cannot be found.</div>'; break; case "pw" : echo '<div class="fail">That Password is wrong for this user.</div>'; break; case "ar" : echo '<div class="fail">To view the page you requested you are required to login.</div>'; break; } ?> |
These are most of the different that could occur such as a missing username, missing password, both missing, wrong password, wrong username, etc. If you find any more please just add them to the list. All this does is check the $_GET['e'] variable for any of the codes I have used. I leave default out simply because I don’t want it to do anything if it doesn’t match one of these codes.
Next up is the script on login.php. It is quite similar to the old script but quite a bit neater and a lot more user friendly:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | function error($type) { header("location: auth.php?e=".$type); exit(0); } require('mysql.class.php'); //include our connection class session_start(); //Start a session //set some variables $user = $_POST['username']; $pass = $_POST['password']; //on with the code if(empty($user)) : if(empty($pass)) : error("upm"); endif; error("um"); elseif(empty($pass)) : error("pm"); endif; |
First we define out error() function to handle all our error calls. This is what triggers the messages on auth.php. We just use the header() function to send it back to the auth.php page with one of our error codes in the url. Pretty simple really.
The next two are self explanatory, we then grab the username and password. Now we check to see if they are empty. We first check if the username is empty, then we check the password. If both are empty we send the both empty error code. If just the username is empty we just send the username empty error code. I think you get the idea by now. For those wondering why I use exit() in the error() function it is because without it the code will continue to the end of the code and then run the header() function. If it comes across another header() function call afterward it will use the last location it finds and not the one you expected. Hopefully that’s about as clear as mud.
19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | $user = preg_replace("/[^a-z0-9 ]/i","",$_POST['username']); $pass = preg_replace("/[^a-z0-9 ]/i","",$_POST['password']); $user = addslashes($user); $pass = addslashes($pass); $db =& new db_mysql("SERVER", "USER", "PASS", "DB"); $r = $db->query("SELECT salt FROM auth WHERE user = '".$user."'"); if($db->num_rows($r) == 1) : $salt = $db->fetch_array($r); $r = $db->query("SELECT pass FROM auth WHERE user = '".$user."' AND pass = '".sha1($salt['salt'].$pass)."'"); if($db->num_rows($r) == 1) : $_SESSION['li'] = true; header("location: content.php"); else : error("pw"); endif; else : error("uw"); endif; |
Next we do some preg_replace() only because it should match whatever character restrictions you have put on the registration system (coming tomorrow hopefully). Then we add slashes to stop anyone harming the database. Next we connect to the db. Now we check to see if the username given matches one in the database, at the same time we try to grab the salt. This saves us a database query and is a little more efficient than calling the database twice. If the username is in the database we then add the salt onto the front of the given password and sha1() encrypt them while checking if both the username and this newly encrypted password match. If this is also true we set out logged in session variable to true and send the user off to the content page. If either of the tests failed they would jump back to the log on page with their respective errors.
A Few Details
Because I have show you the login system first the ’salt’ process may be slightly confusing so I am going to explain how the salting process would work, I will be doing a tutorial tomorrow on how to make a registration system for this but for now a explanation of the salting process should suffice.
The salt is quite simple when you think about it. If you encrypt your password using sha1() all the hacker needs to do is get hold of your MySQL database, not likely but possible, and use a sha1() encoded dictionary and he will eventually crack a password. If you use a ’salt’, the hacker will have to produce a different dictionary for each user he tries to hack meaning he will probably give up before he gets anywhere near cracking a password.
Why will he have to do that I hear you cry? Well the salt is a randomly produced string of numbers & letters generally 8-9 characters long. You pre-pend this random string to the front of your new users password on registration and store that 8-9 letter ’salt’ in your database in a seperate field. This means that each new user will have a different ’salt’, this also means a dictionary attack is practically useless.
I hope that explains how to ’salt’ a password and how it helps make things more secure. Again I’m sorry I can’t give a demo of this script. Oh nearly forgot, protecting pages is the same as last time you add this to the very top of your page, yes even before the doctype:
1 2 3 4 5 6 | <?php session_start(); if($_SESSION['li'] != true) : header("location: auth.php?e=ar"); endif; ?> |
Right, again thanks for reading more tomorrow.
Nearly forgot any problems or bugs leave me a comment and I’ll help sort them ASAP. Oh, also I live in the UK so please remember about the time differences as I’ve had a few inpatient commenter’s who’s comments I’ve had to remove.





