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
}
?>
Related
I'm learning how to hash passwords using PHP for obvious security reasons. I have the two following functions for setting and checking hashes:
function password_encrypt($password)
{
$hash_format = "$2y$10$"; //blowfish
$salt_length = 22;
$unique_random_string = md5(uniqid(mt_rand(), true));
$base64_string = base64_encode($unique_random_string);
$modified_base_64_string = str_replace('+', '.', $base64_string);
$salt = substr($modified_base64_string, 0, $length);
$format_and_salt = $hash_format . $salt;
$hash = crypt($password, $format_and_salt);
echo $hash;
return $hash;
}
function password_check($password, $existing_hash)
{
$hash = crypt($password, $existing_hash);
if($hash === $existing_hash)
{
return true;
}
else
{
return false;
}
}
My issue is that every time I hash a password and send it off to the database it just gets stored as *0. I don't know where the *0 is coming from. Is there something wrong with what I'm doing?
Basically when I use it I just grab the entered password from $_POST and encrypt it using password_encrypt(). But it seems like it always comes out the other side as *0.
Short and Correct Answer
Don't roll your own cryptography, use an implementation that has already been vetted by security experts. For PHP 5.5 and above, this means password_hash() and password_verify(). For earlier versions of PHP, you can use password_compat (if your PHP version is older than 5.4, shame on you for running unsupported versions of PHP).
The Crypto Self-Education Answer
If you're dead set on writing your own cryptography code (for the learning experience, etc.), you must promise to never deploy your unproven implementation and make anything depend on it for security. Otherwise, just use password_hash() and password_verify() and stop reading right now.
There are a lot of problems with your code. Let's go down the line systematically, starting with password_encrypt(). (By the way, you aren't encrypting a password, you're hashing it.)
$unique_random_string = md5(uniqid(mt_rand(), true));
Yeah, NO. This is not a cryptographically secure way to generate a salt for a bcrypt hash. You should take a look at how password_compat implements this feature. Alternatively, see how random_compat generates random bytes for cryptographic purposes. (Also, please tell whomever advised you to use that one-liner to generate random strings that they are playing with fire.)
$unique_random_string = random_bytes(17);
Moving on...
$modified_base_64_string = str_replace('+', '.', $base64_string);
$salt = substr($modified_base64_string, 0, $length);
You forgot an underscore in the second variable name. Also, there is no variable named $length. These lines, corrected, look like so:
$modified_base_64_string = str_replace('+', '.', $base64_string);
$salt = substr($modified_base_64_string, 0, $salt_length);
Next,
$hash = crypt($password, $format_and_salt);
echo $hash;
return $hash;
Please don't leave your echo statements in your example code. Also, you should check for errors and throw an exception so your application doesn't chug happily along if bcrypt hashing fails.
$hash = crypt($password, $format_and_salt);
if ($hash === '*0') {
throw new Exception('Password hashing unsuccessful.');
}
return $hash;
Whew, almost done. Let's look at your password_check() function now:
if($hash === $existing_hash)
{
return true;
}
else
{
return false;
}
Don't compare cryptographic outputs (password hashes, MACs, etc.) using == or ===, or else you invite timing attacks into your application. Use hash_equals() for PHP 5.6, or a hash_equals() polyfill for earlier versions of PHP.
return hash_equals($hash, $existing_hash);
I hope this was an educational experience. Cryptography is hard, don't reinvent the wheel.
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);