Secure registration and log in script - Part 2 of 2 - The Log In 11-11-2013, 07:44 PM
#1
Secure registration and log in script - Part 2 of 2
- The Log in -
- The Log in -
This is the second and final part of this tutorial. If you haven't read the first one I suggest you do that first.
Part 1: http://www.hackcommunity.com/Thread-Tuto...gistration
So, now that the person has registered we want him to be able to log in as well right? So, it's time to write the log in script.
Before we begin there's one thing that should be said. When a log in fails the user should never be prompted with any other message than something similar to "Wrong username and password combination". The reason for this is because we want to return as little information as possible to a malicious user. If you provide info like "No such user" or "Wrong password for that user", you give the attacker valuable information to craft and launch a more specific attack against the site.
That being said, let's continue with the code.
Step 1: Find the username in the database
The first we need to do is to see if we find a matching username in the database. If no match is found we return an error to the user. You can see here that we use almost the same regular expression pattern as we did in the registration. The difference is that we do not validate, but we sanitize which means that we remove any character that does not match the pattern.
Looking at the query, you might wonder why we're not looking for a password. The reason for this is because of the way that the crypt() password matching works. So we first need to find a matching user. We also do not fetch anything else than the id and password. This is because it is the only data we need for this process.
Code:
// Sanitize username to apply to the same rules as the registration script
$username = preg_replace('/[^a-zA-Z0-9_]+/', '', $username);
// Connecto to database and prepare the query
$pdo = new PDO('mysql:dbname=database;host=localhost', 'dbuser', 'dbpass');
$stmt = $pdo->prepare('SELECT id,password FROM users WHERE username=:username');
// Bind the username to the query
$stmt->bindValue(':username', $username);
$stmt->execute();
// Make sure we found a match before continuing
if (!$result = $stmt->fetch(PDO::FETCH_OBJ)) {
// False - No such user was found
return false;
}Step 2: Validate the passwords
If a matching user was found we need to check that the provided password is a match with the stored one. The first thing we need to do is to sanitize the password removing any characters that does not match the rules.
We then get the HMAC key from our previously saved key file and create our HMAC hash.
The last step in this part is validating the hash stored in the database which is what we do in the if statement.
crypt() validation explained:
hmac = the hmac hash we created
stored = the hash we got from the database
Code:
if (crypt(hmac, stored) == stored)This will return true if the provided password is equal to the one used when registering.
Code:
// Sanitize the password to apply with the rules in the registration script
$password = preg_replace('/[^a-zA-Z0-9-|<>$?~]+/', '', $password);
// Get the HMAC key
$key = file_get_contents('/path/to/key.txt');
// Generate HMAC hash
$hmac = hash_hmac('sha512', $password, $key);
// Generate blowfish hash and validate against the password found for the user
if (crypt($hmac, $result->password) != $result->password) {
// False - Password did not match. Deny access
return false;
}Step 3: Create challenge and set new session
Finally we need to create something that we can use to authenticate the user. For this we use a method called Challenge-response Authentication. Basically what this means is that we create a challenge that we make the user verify against.
http://en.wikipedia.org/wiki/Challenge%E...entication
The challenge we create here is a HMAC md5 hash from the users ip + user agent + user id. This challenge will then be used as the session id. We then store the user id in the session. Every time a user wants to do something that requires authentication we then compare the user's response against our session id, but we'll get back to the authentication further down this post.
Code:
// Create challenge and set challenge as new session ID
$challenge = hash_hmac('md5', $_SERVER['REMOTE_ADDR'] . $_SERVER['HTTP_USER_AGENT'] . $result->id, $key);
session_destroy();
session_id($challenge);
session_start();
$_SESSION['user'] = array('id' => $result->id);
// Redirect the user to wherever you want the user to be redirected to
header('Location: /');
// This is here to stop any unwanted execution
die();Full script
Code:
function login($username, $password) {
// Sanitize username to apply to the same rules as the registration script
$username = preg_replace('/[^a-zA-Z0-9_]+/', '', $username);
// Connecto to database and prepare the query
$pdo = new PDO('mysql:dbname=database;host=localhost', 'dbuser', 'dbpass');
$stmt = $pdo->prepare('SELECT id,password FROM users WHERE username=:username');
// Bind the username to the query
$stmt->bindValue(':username', $username);
$stmt->execute();
// Make sure we found a match before continuing
if (!$result = $stmt->fetch(PDO::FETCH_OBJ)) {
// False - No such user was found
return false;
}
// Sanitize the password to apply with the rules in the registration script
$password = preg_replace('/[^a-zA-Z0-9-|<>$?~]+/', '', $password);
// Get the HMAC key
$key = file_get_contents('/path/to/key.txt');
// Generate HMAC hash
$hmac = hash_hmac('sha512', $password, $key);
// Generate blowfish hash and validate against the password found for the user
if (crypt($hmac, $result->password) != $result->password) {
// False - Password did not match. Deny access
return false;
}
// Create challenge and set challenge as new session ID
$challenge = hash_hmac('md5', $_SERVER['REMOTE_ADDR'] . $_SERVER['HTTP_USER_AGENT'] . $result->id, $key);
session_destroy();
session_id($challenge);
session_start();
$_SESSION['user'] = array('id' => $result->id);
// Redirect the user to wherever you want the user to be redirected to
header('Location: /');
// This is here to stop any unwanted execution
die();
}- The Authentication -
Awesome! We've now managed to register the user and let the person log in. But what good is that if you don't have any authentication? So let's write some more code

We now know that if the user is properly logged in the session id will be equal to the HMAC MD5 hash from the visitors ip + user agent + user id. So let's write the code that actually checks this.
Code:
if (isset($_SESSION['user']) && isset($_SESSION['user']['id'])) {
$key = file_get_contents('/path/to/key.txt');
$answer = hash_hmac('md5', $_SERVER['REMOTE_ADDR'] . $_SERVER['HTTP_USER_AGENT'] . $_SESSION['user']['id'], $key);
return ($answer == session_id());
}
return false;Let me explain what's going on here.
The first thing it does is that it verifies the existence of the user and id keys in the $_SESSION array. If one of them are missing it will return false and the user will fail the authentication.
If they are both present it will then grab the key from our key.txt file which is used by the HMAC. Next it creates an answer to the challenge (the session id). If the answer is not equal to the challenge, the user fails authentication. Otherwise the user is successfully authenticated and access to restricted area can be granted.
- The End -
That's it for the two part tutorial on how to write a secure registration and log in script in PHP. I hope you have enjoyed it and if you have any questions of feedback please don't hesitate to write a reply and I will answer to the best of my knowledge

- Happy coding -



![[+]](https://sinister.li/images/modern/collapse_collapsed.png)

![[Image: 120x240.gif]](http://www.gomezpeerzone.com/wp-content/uploads/2011/11/120x240.gif)
![[Image: 2YpkRjy.png]](http://i.imgur.com/2YpkRjy.png)