![]() |
|
Secure registration and log in script - Part 2 of 2 - The Log In - Printable Version +- Sinisterly (https://sinister.li) +-- Forum: Coding (https://sinister.li/Forum-Coding) +--- Forum: PHP (https://sinister.li/Forum-PHP) +--- Thread: Secure registration and log in script - Part 2 of 2 - The Log In (/Thread-Secure-registration-and-log-in-script-Part-2-of-2-The-Log-In) Pages:
1
2
|
Secure registration and log in script - Part 2 of 2 - The Log In - RogueCoder - 11-11-2013 Secure registration and log in script - Part 2 of 2
- 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-Tutorial-Secure-registration-and-log-in-script-Part-1-of-2-The-registration 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%E2%80%93response_authentication 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 - RE: Secure registration and log in script - Part 2 of 2 - The Log In - hellomen - 11-12-2013 Thanks for this usefull tutorial. this definitally get a worth shot at the Image Sharing Software! for sure the extra sha512 hash. I already knew i'd have the same set-up tho but without the usage of the function xD But I didn't think of restricted character usage ![]() as I read in here you use key.txt as salt why don't you use randomized characters in order to make a salt key? (store it on the database as salt) and whenever on login get this key to hash the password
RE: Secure registration and log in script - Part 2 of 2 - The Log In - RogueCoder - 11-12-2013 You can of course use a random salt stored in the database, but it lowers the security. Like I explained in the first part, the key.txt should be stored outside the document root.. The hashing happens in the if statement Code: if (crypt(hmac, stored) == stored)RE: Secure registration and log in script - Part 2 of 2 - The Log In - hellomen - 11-12-2013 (11-12-2013, 08:30 AM)shp0ngl3 Wrote: You can of course use a random salt stored in the database, but it lowers the security. Like I explained in the first part, the key.txt should be stored outside the document root.. Why would it less secure? how would you get always the specific key if you don't store a salt key? like you used key.txt if you locate key.txt you got the key used. also if it was less secure why would businesses like vBulletin - MyBB - PHPBB (forum softwares) use the salt storing on the database? RE: Secure registration and log in script - Part 2 of 2 - The Log In - RogueCoder - 11-13-2013 It's less secure because the salt is stored in the database so a leak will reveal it. Quote:how would you get always the specific key if you don't store a salt key?^-- What do you mean with that --^ there is a key string stored in key.txt (Have you even read part 1?) They don't use it because it's not very user friendly for people who has little to no technical skill. It's easier to just store it in the database. You are speaking of these applications like they are some sort of bullet proof systems that no one can break into. Do you really believe that? RE: Secure registration and log in script - Part 2 of 2 - The Log In - Anima Templi - 11-13-2013 @hellomen your posts in this thread makes think of this:
RE: Secure registration and log in script - Part 2 of 2 - The Log In - hellomen - 11-13-2013 (11-13-2013, 12:33 AM)Anima Templi Wrote: @hellomen your posts in this thread makes think of this:I don't care it makes you think like that. if you want to think it it's your problem I am just asking questions don't like it don't read them simple as fuck isn't it? (11-13-2013, 12:23 AM)shp0ngl3 Wrote: It's less secure because the salt is stored in the database so a leak will reveal it. no I don't believe that but how would a txt file on the server will cause anti leak? as soon as someone finds out about the .txt they have the key string also you are saying now you are using the same key for every password right? aswell as I said I don't believe it vBulletin remains being paid forum software of 250$ and it uses a salt key in the database (each person randomly generated salt...) and I don't think if developers think it's such in-secure that it will be put into a 250$ (or more) paid software is it? in fact how would you protect that .txt file from not getting leaked getting a .txt file is less safe than storing it on the database in my opinion Quote:I mean a .txt file always contains the same keyQuote:how would you get always the specific key if you don't store a salt key?^-- What do you mean with that --^ how would you protect passwords if it's always the same key used to hash passwords...? RE: Secure registration and log in script - Part 2 of 2 - The Log In - Deque - 11-13-2013 I think hellomen has a point here. Salts are not meant to be secret. Their purpose is to render the use of lookup tables, reverse lookup tables and rainbow tables ineffective. But that only works if you use a different salt for every user. It doesn't matter here if that salt is public. If you reuse the salt, two users with the same password will have the same hash and you can build reverse lookup tables for this specific salt. RE: Secure registration and log in script - Part 2 of 2 - The Log In - hellomen - 11-13-2013 (11-13-2013, 09:32 AM)Deque Wrote: I think hellomen has a point here. I guess only you understood me xD thanks for explaining my words better. RE: Secure registration and log in script - Part 2 of 2 - The Log In - RogueCoder - 11-13-2013 I give up.. Seriously, I give up! First you provide a function that uses sha1 and md5 x 3 times or something (Yes I'm going back to the IRC chat now).. Both me and @1llusion tells you that it is NOT secure, and you keep arguing that you're method is secure because you use it more than one time and have salts! Then @Anima Templi tries to help you but you refuse to listen to his suggestion. On top of this you admit that you are new to PHP, and still you have the balls to tell me that after my 13 years of experience I still have no clue what I'm talking about.. Consider this my last time helping you out. Why is it more secure you ask? - When done properly you need two attacks to get to the key. You can even store it on a separate server if you want. So by using the key file you will have to break into the actual server not just dump it with a simple sql injection Code: concat_ws(0x3a,username,password,salt)- The key stored has a much stronger cryptographic entropy than your average salt stored in a database. I mean, this doesn't provide good entropy MyBB's salt generator: Code: function random_str($length="8")
{
$set = array("a","A","b","B","c","C","d","D","e","E","f","F","g","G","h","H","i","I","j","J","k","K","l","L","m","M","n","N","o","O","p","P","q","Q","r","R","s","S","t","T","u","U","v","V","w","W","x","X","y","Y","z","Z","1","2","3","4","5","6","7","8","9");
$str = '';
for($i = 1; $i <= $length; ++$i)
{
$ch = my_rand(0, count($set)-1);
$str .= $set[$ch];
}
return $str;
}I will leave you with a blog post from Mozilla security team about how to store passwords securely, and you will see that the method used is the one described here. http://blog.mozilla.org/webdev/2012/06/08/lets-talk-about-password-storage/ |