I am try to offer the best password safety I can for my program, after some research all roads seem to end at PHPASS as my best option. I'm doing some experimental trials to understand what I'm doing. it's not going well lolAs far as I can see PHPASS adds a random salt to the password and then hashes it so it can then be stored in the DB, The user is now registered!.This works fine for me.It's this part I don't understand,when the user logs in I retrieve the hash from the DB and compare it to the hash that has been made from the password that the user has entered in the login but this time the new hash is different because it has been made from a different salt? and there for doesn't match the one stored in the DB.Somewhere I am being stupid and am missing something obvious that everyone else does get. Please help me as I seem to be retarded!
Yes, this is a good library to use. If you have PHP 5.5+ though, there is no need, as similar functionality is built in.
Use $hasher->CheckPassword($pass, $hash) to check the password, rather than rehashing it. This way it ensures it uses the same salt (the salt is stored at the start of the hash, as is the hash algorithm).
PHPASS returns a hash containing the algorithm used to calculate the hash, the cost of the hashing, the salt used, and what I assume is the result of the algorithm, and thus depends on the password used.
Algorithm:
For bcrypt hashes this should be '$2y$', although '$2x$' and '$2a$' are also possible. See PHP's manual entry on crypt(). (PHPASS uses crypt() if available I believe)
Cost:
A 2 characters wide zero padded value in the 04-31 range. 1<<<cost> will be used internally, which is why 31 is the upper limit (32 bits available).
Salt:
22 characters from the alphabet "./0-9A-Za-z".
The remaining characters won't be used when using the hash as a salt.
Here is some code and its output that might clear up how it works when used correctly:
<?php
echo crypt('meow', '$2y$05$' . str_repeat('.', 20) . '//meow'), PHP_EOL; // salt: ....................//
echo crypt('meow', '$2y$05$' . str_repeat('.', 20) . '//woof'), PHP_EOL; // salt: ....................//
echo crypt('meow', '$2y$05$' . str_repeat('.', 20) . '//oink'), PHP_EOL; // salt: ....................//
echo PHP_EOL, '###########################', PHP_EOL, PHP_EOL;
echo $meow = crypt('meow', '$2y$05$' . str_repeat('.', 20) . '/meow'), PHP_EOL; // salt: ..................../m
echo $woof = crypt('meow', '$2y$05$' . str_repeat('.', 20) . '/woof'), PHP_EOL; // salt: ..................../w
echo $oink = crypt('meow', '$2y$05$' . str_repeat('.', 20) . '/oink'), PHP_EOL; // salt: ..................../o
echo PHP_EOL, '###########################', PHP_EOL, PHP_EOL;
echo crypt('meow', $meow), PHP_EOL; // salt: ..................../m
echo crypt('meow', $woof), PHP_EOL; // salt: ..................../w
echo crypt('meow', $oink), PHP_EOL; // salt: ..................../o
echo PHP_EOL, '###########################', PHP_EOL, PHP_EOL;
var_dump(
$meow === crypt('meow', $meow),
$woof === crypt('meow', $woof),
$oink === crypt('meow', $oink)
);
$2y$05$..................../.0/SFonSqiMg1jG/nWVk278LFl597ZlC
$2y$05$..................../.0/SFonSqiMg1jG/nWVk278LFl597ZlC
$2y$05$..................../.0/SFonSqiMg1jG/nWVk278LFl597ZlC
###########################
$2y$05$..................../eZ/qB29TfhKdQOwZTIrLyzBTMYOwupD6
$2y$05$..................../u45Ujx7ViT/dCkbXPqwsVuv407AA/99a
$2y$05$..................../eZ/qB29TfhKdQOwZTIrLyzBTMYOwupD6
###########################
$2y$05$..................../eZ/qB29TfhKdQOwZTIrLyzBTMYOwupD6
$2y$05$..................../u45Ujx7ViT/dCkbXPqwsVuv407AA/99a
$2y$05$..................../eZ/qB29TfhKdQOwZTIrLyzBTMYOwupD6
###########################
bool(true)
bool(true)
bool(true)
Thanks for your help peeps i seem to have it working now but if you would be so kind as to check it for me, if it's OK others may find it helpful, Please keep in mind it's only testing files so more will be added if the core is OK.
This is the REG code that hashes the password and stores it.
if(isset($_POST['submit'])) {
$username = $_POST['username'];
$password = $_POST['password'];
$hash = password_hash($password, PASSWORD_BCRYPT);
if (empty($_POST) === false){
mysql_query("INSERT INTO `accounts` VALUES ('', '$username', '$hash') ");
echo 'Done';
}else{
echo 'Not Done';
}
} ?>
And this is the Login
<?php
require('../password/lib/password.php');
require_once("connect.php");
if(isset($_POST['submit'])) {
$username = $_POST['username'];
$safe_username = mysql_real_escape_string($username);
$password = $_POST['password'];
$query = "SELECT * FROM accounts WHERE username = '$safe_username'";
$result = mysql_query($query);
$row = mysql_fetch_assoc($result);
$hash = $row['password'];
if (password_verify($password, $hash)) {
echo 'Logged in';
} else {
echo 'Not logged in';
}
} else {
echo " Not submitted";
}
What do you think?
Related
My first question here, so hopefully this all goes well.
I have an application that is currently running on desktop under MS Access using VBA code. When the user logs in I have a function to encrypt the password. This is:
Public Function EncryptEasy(strIn As String) As String
Dim strChr As String
Dim i As Integer
Dim Salt As Long
Salt = 543214321
For i = 1 To Len(strIn)
strChr = strChr & CStr(Asc( Mid(strIn, i, 1) ) Xor Salt)
Next I
EncryptEasy = strChr
End Function
To give you an idea, when I run EncryptEasy("test") in VBA, it returns:
543214213543214228543214210543214213
I am now setting up a simple web-app using PHP and was hoping to utilise the same encryption as I'm currently using by coding this function into PHP. I HAVE tried and below is my attempt:
function EncryptEasy($strIn) {
$salt = 543214321;
$strChr = Null;
$strLen = strlen($strIn);
for ($i = 0; $i <= $strLen; $i++) {
$strChr = $strChr . chr(ord(substr($strIn, $i, 1)) xor $salt);
}
return $strChr;
}
However, this is returning blank. I have tried echoing this:
<?php
echo EncryptEasy("test");
?>
to no avail.
Is anyone able to see where I am going wrong?
I would not suggest creating your own algorithm for encrypting, instead use built-in functions provided by php.
Although this is not encrypting the password like the one in your original code, using password_hash is easier than creating your own, and to verify use password_verify()
Hashing Password:
<?php
$hashed = password_hash("somePassword", PASSWORD_DEFAULT);
?>
Verifying Password
<?php
if(password_verify('somePassword', $hashed))
{
echo 'Same'; //goes here
} else {
echo 'Not same';
}
if(password_verify('somePasswordsss', $hashed))
{
echo 'Same';
} else {
echo 'Not same'; //goes here
}
?>
I am playing around with Blowfish algorithm today. Everything works fine but sometimes my program fails as Blowfish Algo gives *0 as an output,so whole of the logic after that tumbles down.
On PHP's official documentation page for CRYPT under changelog it is mentioned that this issue has been solved in version 5.3.2
5.3.2 Fixed Blowfish behaviour on invalid rounds to return "failure" string ("*0" or "*1"), instead of falling back to DES.
I am using PHP 5.5.9-1ubuntu4.2 and still facing the same issue. Here is the program:
<?php
$password = 'Rajat';
$userpassword = 'Rajat';
echo $password;
echo "\n";
echo "Salt: ";
$salt = substr(uniqid(rand()),0,22);
echo $salt;
echo "\n";
echo "Using Blowfish: ";
$bf = crypt($password,'$2y$10$'.$salt);
echo $bf;
echo "\n";
echo "Starting password checking...\n";
$full_bf_salt = substr($bf,0,29);
$verify_hash = crypt($userpassword,$full_bf_salt);
echo "Verified calculated hash: ".$verify_hash;
if($verify_hash==$bf){
echo "Password is correct\n";
}else{
echo "Password is incorrect\n";
}
?>
Most of the time,crypt function works as expected but it fails sometimes.Does anyone know why or is there something I have implemented in a wrong way?
I managed to reproduce the error with your code. You get *0 as a result when the length of the generated salt is less than 22. In my case the length was 21 characters.
Salt: string(21) "6948531853de8c4cd7a85"
Using Blowfish: *0
Bcrypt expects a 128 bit salt encoded in a base64 format, resulting in 22 characters of salt. When the salt is not valid, *0 is returned to indicate that there was an error.
Here is one way to generate the salt properly using mcrypt_create_iv():
$raw_salt_len = 16;
$required_salt_len = 22;
$salt = mcrypt_create_iv($raw_salt_len, MCRYPT_DEV_URANDOM);
$base64_digits = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
$bcrypt64_digits = './ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
$base64_string = base64_encode($salt);
$salt = strtr(rtrim($base64_string, '='), $base64_digits, $bcrypt64_digits);
if (function_exists('mb_substr')) {
$salt = mb_substr($salt, 0, $required_salt_len, '8bit');
} else {
$salt = substr($salt, 0, $required_salt_len);
}
I extracted this code from the password_compat library. I highly recommend to study its code. It covers cases when mcrypt_create_iv() is not available, etc.
New to stackoverflow :)
I just now started to use a bcrypt function I've found on some site about security. I've never really worried about the output from this until our technician at work said this to me:
The salt seems to always be in the front of every password.
Is this correct or have I made a major boo boo? :)
The code I use is this:
<?php
function bcrypt($password, $salt, $rounds=12) {
// Check if bcrypt is available on the server
if (CRYPT_BLOWFISH != 1) {
throw new Exception("bcrypt stöds inte. Se http://php.net/crypt");
return;
}
// Check that rounds are within the allowed range
if ($rounds < 4)
$rounds = 4;
else if ($rounds > 12)
$rounds = 12;
// Create a prefix to tell the crypt that we want to use bcrypt
$salt_prefix = sprintf('$2a$%02d$', $rounds);
// Check if the salt contains invalid characters:
if (!preg_match('#^[A-Za-z0-9./]{22}$#', $salt)) {
// The salt is not bcrypt-safe. Redo to 22 characters (A-Za-z0-9. /)
$new_salt = base64_encode($salt);
if (strlen($new_salt) < 22)
$new_salt .= base64_encode(md5($salt));
$salt = substr($new_salt, 0, 22);
$salt = str_replace(array('+', '-'), '.', $salt);
$salt = str_replace(array('=', '_'), '/', $salt);
}
// hash the password with bcrypt
return crypt($password, $salt_prefix.$salt);
}
// Examples :
echo "Bcrypt: ". bcrypt('abc', 'QyrjMQfjgGIb4ymtdKQXIr', 12);
?>
This will output:
Bcrypt: $2a$12$QyrjMQfjgGIb4ymtdKQXIewDBqhA3eNppF8qOrMhidnEbzNvmHqhy
As you can see the salt is inside the password now "bold text":
Salt = QyrjMQfjgGIb4ymtdKQXIr
pass = $2a$12$QyrjMQfjgGIb4ymtdKQXI
ewDBqhA3eNppF8qOrMhidnEbzNvmHqhy
This seem to be the same every time regardless of salt. Salt is always included except the last character?
You can test an existing bcrypt hash with lots of tools online, like this bcrypt generator
For authentification with Dovecot, I use SSHA256 hashes but I have no clue how to validate a given password against the existing hash. The following PHP functions (found them in the web) are used to create the SSHA256 hash:
function ssha256($pw) {
$salt = make_salt();
return "{SSHA256}" . base64_encode( hash('sha256', $pw . $salt, true ) . $salt );
}
function make_salt() {
$len = 4;
$bytes = array();
for ($i = 0; $i < $len; $i++ ) {
$bytes[] = rand(1,255);
}
$salt_str = '';
foreach ($bytes as $b) {
$salt_str .= pack('C', $b);
}
return $salt_str;
}
Example output: {SSHA256}lGq49JTKmBC49AUrk7wLyQVmeZ7cGl/V13A9QbY4RVKchckL
Do I have to extract the salt, but how?
I totally lost the way for solving the problem, has anyone a hint for this?
Thanks to everyone for helping!
Oh and sorry, I have to use SSHA256, because Dovecot 1.2.15 supports only those schemes:
CRYPT MD5 MD5-CRYPT SHA SHA1 SHA256 SMD5 SSHA SSHA256 PLAIN CLEARTEXT CRAM-MD5 HMAC-MD5 DIGEST-MD5 PLAIN-MD4 PLAIN-MD5 LDAP-MD5 LANMAN NTLM OTP SKEY RPA
You should not be using the SHA family for password hashing. They are fast and designed for hashing files at speed. You need pashword hashing to be expensive. Use bcrypt, PHPass or just use this class, which I rolled myself (but not until you learn to pick holes in it):
class PassHash {
public static function rand_str($length) {
$total = $length % 2;
$output = "";
if ($total !== 0) {
$count = floor($length / 2);
$output .= ".";
} else $count = $length / 2;
$bytes = openssl_random_pseudo_bytes($count);
$output .= bin2hex($bytes);
// warning: prepending with a dot if the length is odd.
// this can be very dangerous. no clue why you'd want your
// bcrypt salt to do this, but /shrug
return $output;
}
// 2y is an exploit fix, and an improvement over 2a. Only available in 5.4.0+
public static function hash($input) {
return crypt($input, "$2y$13$" . self::rand_str(22));
}
// legacy support, add exception handling and fall back to <= 5.3.0
public static function hash_weak($input) {
return crypt($input, "$2a$13$" . self::rand_str(22));
}
public static function compare($input, $hash) {
return (crypt($input, $hash) === $hash);
}
}
You have to hash the plaintext given and compare that hash against one you have stored. The salts are stored in the hashes, and should be random. If you like, add a pepper. You should also make the workrate variable, so that you can change the workrate at any moment when needed and still have your system work.
If, like you say, you have no way of implementing this, you can unpack the hash as follows:
function unpack_hash($hash) {
$hash = base64_decode($hash);
$split = str_split($hash, 64);
return array("salt" => $split[1], "hash" => $split[0]);
This is because SHA256 is 256 bits, or 64 hex characters. You can just always assume the first 64 chars are the hash
You need to store the salt along with the hashed value.
When you need to validate the password, you simply calculate the hash again with the user input password + the stored salt. If the hashes match, the user entered the correct password.
For your format, use base64_decode first, the last 4 bytes of the result will be the salt.
I have made an password hashing script using this and this, i am getting it to work correctly except some times the crypt function is giving hash as "*0", and then it fails.
PHP Codes
$password='password';
$salt = '$2y$07$';
$salt .= base64_encode(mcrypt_create_iv(16, MCRYPT_DEV_RANDOM));
$salt .='$$';
$password_hash = crypt($password, $salt)';
echo $password_hash.'<br />';
Using above i am getting values as
$salt = '$2y$07$8K3i8rJ7n7bsJA36CfbabQ==$$';
$crypt_password = $password_hash;
$crypt_password = '$2y$07$8K3i8rJ7n7bsJA36CfbabO9ojj2hl61azl8CubJQhRTgla4ICiCVC';
if (crypt($password,$crypt_password)===$crypt_password)
{
echo 'password verified';
}
else{
echo 'password NOT verified';
}
Please see and suggest any possible way to make it work correctly.
Thanks.
The problem is that base64_encode may generate a string with '+' symbol, which is considered an incorrect salt by crypt function.
var_dump your $salt along with $password, and you'll see that each time + character is used in the salt, crypt function will return a '*0' string - the sign of failure.
One possible way of solving it is replacing all '+' signs with '.':
$salt = str_replace('+', '.', $salt);