Been awhile. Still slacking. I got a question about user login. I'm trying to learn one of the password encryption methods from the php manual, but my reversal method has an error. I keep getting sent back to homepage with error. Details are typed correctly, so it must be the second error firing: $passwordHash hash doesn't equal $dbPasswordHash. Reg Script if($error == '') { $user = new pUser; $regDate = date('d-m-Y'); $user->get_ip(); $ip = $user->return_ip(); $salt = mcrypt_create_iv(22, MCRYPT_DEV_URANDOM); $salt = base64_encode($salt); $salt = str_replace('+', '.', $salt); $passwordHash = crypt($_POST['password'], '$2y$10$'.$salt.'$'); // create vercode $verCode = md5($_POST['username'] . $_POST['firstName']); // register the user $db = new PDO($dsn, $dbUserName, $dbPassword, array(PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION)); try{ //Insert user into users $stm = $db->prepare('INSERT INTO `users`(`username`, `firstName`, `lastName`, `displayName`, `email`, `membership`, `verCode`, `passwordHash`, `salt`, `regDate`, `ip`) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)'); $stm->execute(array($_POST['username'], $_POST['firstName'], $_POST['lastName'], $_POST['firstName'] . ' ' . $_POST['lastName'] , $_POST['email'], 'none', $verCode, $passwordHash, $salt, $regDate, $ip)); // insert user into profiles try{ $profileStm = $db->prepare('INSERT INTO `profiles`(`username`, `about`, `company`, `country`, `status`, `profileViews`, `image`, `useDisplayName`) VALUES (?, ?, ?, ?, ?, ?, ?, ?)'); $profileStm->execute(array($_POST['username'], 'blank', 'blank', 'blank', 'set info in dashboard', 0, '', 'n')); }catch(\PDOException $e){} //send verCode // Set up user directory }catch(\PDOException $e){ } // Return to index with message. }else{ // return to homepage with error. header('Location: ../../../index.php?error='.urlencode($error).''); exit; } PHP: Login Script if($_SERVER['REQUEST_METHOD'] === 'POST') { require('../../data/sqldata.php'); require('../classes/pUser.php'); $user = new pUser; $user->set_user_name($_POST['username']); $dbUsername = $user->return_username(); $dbPasswordHash = $user->return_password_hash(); $dbSalt = $user->return_salt(); // test if user was found if($dbUserName != '' || !empty($dbUsername)) { // return to index with user not found error. header('Location: ../../index.php?error='.urlencode('User details could not be found').''); exit; } // test if entered password matches database info $passwordHash = crypt($_POST['password'], '$2y$10$'.$dbSalt.'$'); if($passwordHash === $dbPasswordHash) { // user has logged in $_SESSION['ID'] == $user->return_id(); header('Location: ../../index.php?error='.urlencode('User details could not be found').''); exit; }else{ // return to index with user could not be found. header('Location: ../../index.php?error='.urlencode('User details could not be found').''); exit; } }else{ // Go to index, or page not found. header('Location: ../../index.php'); exit; } PHP:
First off, it should be noted that crypt() is considered insecure and in every way inferior to password_hash() for hashing passwords. Using password_hash() would also eliminate the need for generating and storing a salt, as the salt becomes part of the hash string. Comparison can then done with password_verify(), which is safe against timing attacks; or direct hash comparison like you've done originally. Aside from that, your best bet would be to var_dump() $passwordHash and $dbPasswordHash and compare them, then work backwards from there.
Ugh, thanks for the tip. I guess I'll just Google this method you mentioned since it's better. I'll post back if that method gives me trouble. Thanks again man, appreciate it.
No problem. There's a lot of bad, outdated information out there surrounding PHP, especially with regards to tutorials. I recommend phptherightway.com, they also have a section on password hashing.
I'll check that out. I've got this working now using the method you suggested. Only a couple of functions too. I'm a little slow with stuff I haven't tried, and coding in general. You look new here, haha. I've been here a little while. Usually Popsicle helps out... I hire when I get over my head: stuff like pagination, and what not... I guess I wasn't born to be a genius, lol.
I've known about this site for years, just never bothered to join until now. As far as PHP goes -- if you've gotten this far, you're doing comparatively well, and anyone better has been in the exact same place at some point.
Hm... I use a convoluted crypt()-function to create and check passwords - basically, one inputs a password (minimum of 8 characters, some other limits may be imposed as well), then simply do as follows: function passwordHash($password) { $salt = crypt($password); $password = crypt($password, $salt); return $password; } Code (markup): I'm not sure why it's being done this way, actually - made this bit a long time ago, and completely forgot that I was thinking of redoing it. For checking the password, I basically just query the database based on user-id on login, and do as follows: //query the database based on user-id $dbpassword = $result['password']; if (crypt($password, $dbpassword) == $dbpassword) { do stuff } Code (markup): I'm just wondering if I should perhaps rewrite that using password_hash() instead. Thoughts?
I'd recommend it. It would not only simplify things, but it'd also make your code more secure and future-proof. When you're comparing password hashes it's important that you don't give away too much information on the amount of time taken to hash a given password, as that makes you vulnerable what are known as timing attacks. When using crypt() for instance, the hash_equals() function should be used to make the comparison instead of doing so directly. The function works by padding the time it takes to do the comparison so that an attacker can't gain meaningful information from differences in response times. When using password_hash(), password_verify() is used to make the timing-safe comparison.
I think the reason I chose crypt() was that password_hash / password_verify is a PHP 5.5 only solution. As far as I remember the host this specific solution was running on was running 5.3 or 5.4. That is probably the reason. Dunno why I didn't incude both, though... must have been a brain-fart, or just rush to get it out the door