I have a big problem, I have to write a php login page using an db where password are stored as PBKDF2 (with another perl script). When I get the password with a query I read this:
sha256:1000:2SeBDP88w4bqKbJaCJNpNuRHQhUM96X1:jgh/SZtmRWH5iDIwtXyFLtuuDf7YE+7HQEJZ4KFFNAg= (I know this password but I cannot regenerate it in php).
I tried with this script (get from php.net):
$password = "qqqqq";
$iterations = 1000;
$salt = "2SeBDP88w4bqKbJaCJNpNuRHQhUM96X1";
$hash = hash_pbkdf2("sha256", $password, $salt, $iterations, 20);
echo $hash // result a2ba3349194c38f828af
but the pass generate is a2ba3349194c38f828af and not jgh/SZtmRWH5iDIwtXyFLtuuDf7YE+7HQEJZ4KFFNAg=
who wrote the perl script that store these password told me "The passwords are getting encoded though one-way hashing scheme named 'PBKDF2'"
Some ideas? Someone know where I'm wrong?
jgh/SZtmRWH5iDIwtXyFLtuuDf7YE+7HQEJZ4KFFNAg= is Base64 (the = on the end is a dead giveaway, though Base64 exists without a trailing =).
Converted to hex, the value is 8E087F499B664561F9883230B57C852EDBAE0DFED813EEC7404259E0A1453408
This is still not your answer, but now we can easily see it is 64 hex characters => 32 bytes.
You asked for 20 bytes.
It also looks like your salt input is base64, but the function you're passing it to expects ... whatever the output of base64_decode is.
So, you need to consistently process the base64-encoded data. And then you'll need to make sure that your hash algorithm, iteration count, and output byte count all match what the perl script says.
try this
$hash = strtoupper(bin2hex($hash));
Related
I am creating a PHP-version of a login script that is in ASP.net/VB.net. The database where the usernames and passwords are stored is in MSSQL and that will not change. Passwords are stored as binary data. Here's a part of the code in VB.net that compares the password to the one saved in the database.
Dim sha1 As sha1 = sha1.Create()
Dim password As Byte() = sha1.ComputeHash(Encoding.Unicode.GetBytes(Me.txtPwd.Text))
CustData = .GetCustomerByEmail(Me.txtUser.Text, password)
I found out that it's part of a library.
I am encountering problems after hashing the password in PHP's sha1. I don't know how to convert the hashed string to a binary that I could use to compare it to the database.
As an example, the password "Test100" in the MSSQL database has a value of (after unpack('H*', $binaryPassword)) 7397001ce5259b79c436a369b9d3a8c7bc2a85385fdec57a. I am not sure how to get that string above from the hashed password I am getting using PHP.
Here's what I have in PHP so far and where I am stuck in.
$password = sha1(mb_convert_encoding($_POST['password'], 'utf-16le'), false); // $_POST['password'] = 'Test100'
The output for that is 8415ec8cc9287a10f81db5a77341709d304bfa92.
So I am thinking there's one more step to change $password to $binaryPassword or the hexadecimal representation of the binary password.
From this page, it's saying Byte is an 8-bit unsigned integer, but not really sure how to get that from a PHP string.
Thanks in advance!
What happens if you try to run the sh1 on the static text "Test100"?
ok, so I'm trying to create some password protected web pages using PHP 5.4 script and MySQL. Running a sample produced some unexpected results!
I created the password using:
$salt = "";
$salt.=substr("./ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",mt_rand(0,63), 1);
$salt .= substr("./ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789", mt_rand(0, 63), 1);
$pw1 = crypt($pw,$salt);
and stored the $pw1 in MySQL database.
then in my authentication pages, I retrieved the db password and used user input from a form to validate the user, checking like this:
get $db_pw from database then...
if (crypt($user_password,$db_pw) == $db_pw) ... proceed
All worked fine, but if I enter a string longer than the password it still passed thru ok. For example: if my password was "stack", if I entered "stackoverflow" it would pass ok. If I entered "stac" it would stop as intended.
I've tried searching on this but could not find anything similar.
What am I doing wrong?
It looks like the crypt function has this little caveat:
The standard DES-based crypt() returns the salt as the first two characters of the output. It also only uses the first eight characters of str, so longer strings that start with the same eight characters will generate the same result (when the same salt is used).
So I presume that what happens does not happen on "stack" and "stackoverflow" but does with e.g. "stackove" and "stackoverflow".
Using crypt() is a really bad idea. Use PBKDF2 or bcrypt instead of crypt of the stone age.
note: i am not looking for workaround, i am looking for a plain ruby solution!
this question is the similar to this question, but it isn't answerd, its just a workaround to a shell commando there.
i want to generate a sha512 encrypted string which is compatible with the format in debian /etc/shadow.
the following create a correct string with php:
$salt = 'fGn9LR75';
$hash = crypt('test', '$6$'.$salt);
// hash is:
// $6$fGn9LR75$YpI/vJHjEhvrYp5/eUSRinpiXdMthCxFWSEo0ktFNUaRBsA7pCWYzzmQptmnfyHno9YEJFNHYuESj3nAQmSzc1
as far as i know this a normal, salted base64 encoded string. the spec of the sha generation method is here
irb(main):001:0> salt = 'fGn9LR75';
irb(main):002:0* hash = 'test'.crypt('$6$' + salt);
irb(main):003:0* hash
=> "$6$fGn9LR75$YpI/vJHjEhvrYp5/eUSRinpiXdMthCxFWSEo0ktFNUaRBsA7pCWYzzmQptmnfyHno9YEJFNHYuESj3nAQmSzc1"
The crypt() algorithm for SHA256/512 is not simply a base64-encoded hash. It's an intentionally crazy process which involves multiple hashes running in parallel.
I need to get the basics of this function. The php.net documentation states, for the blowfish algorithm, that:
Blowfish hashing with a salt as follows: "$2a$", a two digit cost parameter, "$", and 22 base 64 digits from the alphabet "./0-9A-Za-z". Using characters outside of this range in the salt will cause crypt() to return a zero-length string
So this, by definition, should not work:
echo crypt('rasmuslerdorf', '$2a$07$usesomadasdsadsadsadasdasdasdsadesillystringforsalt$');
However, it spits out:
$2a$07$usesomadasdsadsadsadaeMTUHlZEItvtV00u0.kb7qhDlC0Kou9e
Where it seems that crypt() has cut the salt itself to a length of 22. Could somebody please explain this?
Another aspect of this function I can't get my head around is when they use crypt() to compare passwords. http://php.net/manual/en/function.crypt.php (look at ex. #1). Does this mean that if I use the same salt for all encrypting all my passwords, I have to crypt it first? ie:
$salt = "usesomadasdsadsadsadae";
$salt_crypt = crypt($salt);
if (crypt($user_input, $salt) == $password) {
// FAIL WONT WORK
}
if (crypt($user_input, $salt_crypt) == $password) {
// I HAVE TO DO THIS?
}
Thanks for your time
Following code example may answer your questions.
To generate hashed password using Blowfish, you first need to generate a salt, which starts with $2a$ followed by iteration count and 22 characters of Base64 string.
$salt = '$2a$07$usesomadasdsadsadsadasdasdasdsadesillystringfors';
$digest = crypt('rasmuslerdorf', $salt);
Store the whole $digest in database, it has both the salt and digest.
When comparing password, just do this,
if (crypt($user_input, $digest) == $digest)
You are reusing the digest as salt. crypt knows how long is the salt from the algorithm identifier.
New salt for every password
$password = 'p#ssw0rd';
$salt = uniqid('', true);
$algo = '6'; // CRYPT_SHA512
$rounds = '5042';
$cryptSalt = '$'.$algo.'$rounds='.$rounds.'$'.$salt;
$hashedPassword = crypt($password, $cryptSalt);
// Store complete $hashedPassword in DB
echo "<hr>$password<hr>$algo<hr>$rounds<hr>$cryptSalt<hr>$hashedPassword";
Authentication
if (crypt($passwordFromPost, $hashedPasswordInDb) == $hashedPasswordInDb) {
// Authenticated
Quoting from the manual
CRYPT_BLOWFISH - Blowfish hashing with
a salt as follows: "$2a$", a two digit
cost parameter, "$", and 22 base 64
digits from the alphabet
Note: 22 base 64 digits
BCrypt uses 128 bits for salt, so 22 bytes Base64, with only two bits of the last byte being used.
The hash is computed using the salt and the password. When you pass the crypted password, the algorithm reads the strength, the salt (ignoring everything beyond it), and the password you gave, and computes the hash, appending it. If you have PostgreSQL and pg_crypto handy, SELECT gen_salt('bf'); will show you what of $salt is being read.
Here's a code sample for salt generation, from my .NET implementation's test-vector-gen.php, alternatively:
$salt = sprintf('$2a$%02d$%s', [strength goes here],
strtr(str_replace(
'=', '', base64_encode(openssl_random_pseudo_bytes(16))
),
'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/',
'./ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'));
There is no reason to use the same salt for all of your passwords. The salt is part of the output anyway so you gain nothing in convenience... though I grant PHP ought to have a built-in gen_salt function.
First question:
So this, by definition, should not work:
echo crypt('rasmuslerdorf', '$2a$07$usesomadasdsadsadsadasdasdasdsadesillystringforsalt$');
Where it seems that crypt() has cut
the salt itself to a length of 22.
Could somebody please explain this?
There isn't a problem with having too many characters... the phrase Using characters outside of this range in the salt will cause crypt() to return a zero-length string referse to outside the range of base 64 not the range of 22 characters. Try putting an illegal character in the salt string, and you should find that you get an empty output (or if you put < 22 characters in, resulting in illegal empty bytes).
Second question:
You pass in the encrypted stored password as salt because the salt string always appears (by design) in the encrypted string, and this way you ensure that you have the same salt for both encryption of stored and user-entered password.
This question is in relation to my response to ZZ Coder's answer. Basically my question is regarding storing the crypt() result in the database. Am I supposed to store the entire output in the database, so that my database looks like this:
--------------------------------------------------------------------------------
| ID | Username | Password |
--------------------------------------------------------------------------------
| 32 | testuser | $2a$07$usesomadasdsadsadsadaeMTUHlZEItvtV00u0.kb7qhDlC0Kou9e |
--------------------------------------------------------------------------------
If yes, then doesn't this kind of defy the purpose of using a salt in the first place? If someone gains access to the db, they can clearly see the salt used for the encryption?
Bonus question: Is it secure to use the same salt for every password?
I'm wanting to store hashed passwords in MySQL, I'm using PHP:
<?php
$salt = '!£$%^&*()#';
$username = 'abc';
$password = '123';
$hash = hash('sha1', $username . $salt . $password, true);
?>
The true parameter in hash() will return the value as raw binary data. But I don't understand what this means exactly. How should it be correctly stored in MySQL?
I found the solution.
Normal (hexed) hashes of sha1() are always CHAR(40) in length. When you return a hash as raw binary data in php, it will return a string as CHAR(20), saving 50% database space yet representing the exact same value. This is because 2 characters of hex can be compressed into 1 character, thus halving it the space needed.
So store the password as CHAR(20) and use the *_bin collation.
The last parameter of the hash() function indicates how the hash value is returned:
Either as raw binary data. This way
you get the direct output of the
specific hash function you're using, in this case sha1.
Or as a string containing a hexadecimal representation of the same raw binary data.
They are both the same and differ only in their representation. Unless you have a good reason, I would suggest that you use the hexadecimal representation and store it as a string in your database. This way it is much easier to debug problems, since you could easily print out the hexadecimal hash value.
If you do want to store a raw binary string in MySQL, declare the column BINARY(16) if it's of a known fixed length of 16 bytes, VARBINARY(32) if it's of variable length up to 32 bytes, or one of the BLOB types for binary fields that potentially get very long (e.g., BLOB up to 64K, LONGBLOB up to 4G).
The "normal" way of doing this, AFAIK, is to use the addslashes() function.
e.g.:
$hash = hash('sha1', $username . $salt . $password, true);
$query_safe_hash = addslashes($hash);
$query_safe_username = addslashes($username);
$query = "INSERT INTO DBTable(username, password) VALUES ('$query_safe_username', '$query_safe_hash')";
mysql_query($query) or die("Failed to store credentials!");
Side note: from a crypto best practices standpoint, the salt should be a known length, should be generated randomly, and prepended to your hash before being stored to the database. Something like
$salt = generate_random_salt();
$query_safe_hash = addslashes($salt) . addslashes(hash('sha1', $salt . $username . $password, true);
Then to verify the user's credentials, you retrieve the stored hash, remove the slashes, and strip the known salt length from the beginning of the stored hash and use the same salt to generate a hash of the provided credentials, then compare. This helps harden your hash algo against various cryptanalysis attacks (in particular, differential cryptanalysis).