Reputation: 204
I have decided recently to use a more secure encrytion for my password. I have no problem encrypting my password, after calling mc_encrypt($encrypt)
the method returns an encrypted password.
When doing decryption by calling mc_decrypt($decrypt)
, the method returns false. As you can see in the mc_decrypt($decrypt)
method, there is an if statement near the bottom. I cannot get the if statement to pass. Does anyone know what I can change to get $calcmac!==$mac
to return true? Thanks
<?php
class Encrypt {
public $encryptionKey = 'xxxxxxx';
public function __construct() {
define('ENCRYPTION_KEY', $this->encryptionKey);
}
// Encrypt Function
public function mc_encrypt($encrypt){
$encrypt = serialize($encrypt);
$iv = mcrypt_create_iv(mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_CBC), MCRYPT_DEV_URANDOM);
$key = pack('H*', $this->encryptionKey);
$mac = hash_hmac('sha256', $encrypt, substr(bin2hex($key), -32));
$passcrypt = mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $key, $encrypt.$mac, MCRYPT_MODE_CBC, $iv);
$encoded = base64_encode($passcrypt).'|'.base64_encode($iv);
return $encoded;
}
// Decrypt Function
public function mc_decrypt($decrypt){
$decrypt = explode('|', $decrypt);
$decoded = base64_decode($decrypt[0]);
$iv = base64_decode($decrypt[1]);
$key = pack('H*', $this->encryptionKey);
$decrypted = trim(mcrypt_decrypt(MCRYPT_RIJNDAEL_256, $key, $decoded, MCRYPT_MODE_CBC, $iv));
$mac = substr($decrypted, -64);
$decrypted = substr($decrypted, 0, -64);
$calcmac = hash_hmac('sha256', $decrypted, substr(bin2hex($key), -32));
if($calcmac!==$mac){ return false; }
$decrypted = unserialize($decrypted);
return $decrypted;
}
}
?>
Upvotes: 0
Views: 2209
Reputation: 11999
Sorry, encrypting passwords with a secret stored on the server isn't secure: If an intruder breaks into your codebase, intruder can retrieve each and any password using your codebase and the stored password (somewhere in code or your persistent store).
OWASP, the Online Web Application Security Project, provides well prepared documents: Password Storage Cheat Sheet, Authentication Cheat Sheet and PHP Security Cheat Sheet. Have a look!
The way to go is a salted hash.
How to handle newly created passwords
hash = HashFunction( password, salt )
with a random saltHow to verify password
hash = HashFunction( enteredPassword, salt )
Why use a hash operation?
A hash operation is a so called trapdoor function: While you can compute the function easily, it's hard to compute the reverse function. Corollary: It's easy to compute a password-hash from a password, but it's hard to compute the password from the password-hash.
PHP's hash functions
These days, PHP's PBKDF2 is first choice for password hashes [Wikipedia on PBKDF2].
If your PHP installation is too old, even salted hashes with md5()
are better than two-encrypted password. But only in case definitely nothing else is available!
Sample of PBKDF2 usage
function getHashAndSaltFromString( $password ) {
// choose a sufficiently long number of iterations
// ... to make the operation COSTLY
$iterations = 1000;
// Generate a random IV using mcrypt_create_iv(),
// openssl_random_pseudo_bytes() or another suitable source of randomness
$salt = mcrypt_create_iv(16, MCRYPT_DEV_RANDOM);
$hash = hash_pbkdf2("sha256", $password, $salt, $iterations, 20);
return array( $hash, $salt );
}
Beautiful side effect of hash usage
Many websites do restrict the length of passwords to a certain length - quite likely due to the length of the underlying persistent storage [= length of field in a database table].
If you use the hash-based password storage technique, your users might use passwords of arbitrary length!
Since the hash is of constant length and you only persist password and salt, the length restriction is superfluous. Support long passwords in your web-app!
At an extreme case, you could even allow users to upload a file - e.g. a picture of their home - as a credential [=password].
Side note on random sources in PHP's MCRYPT functions
Note, that PHP does provide two sources of randomness MCRYPT_DEV_RANDOM
and MCRYPT_DEV_URANDOM
.
MCRYPT_DEV_RANDOM
gets its randomness from /dev/random
MCRYPT_DEV_URANDOM
gets its randomness from /dev/urandom
/dev/urandom
provides random data immediately and non-blocking each time you query it, /dev/random
might block (take some time to return).
Therefore, at first sight, /dev/urandom
and MCRYPT_DEV_URANDOM
might be better suited for random number generation. In fact, it is not!
/dev/random
might block request up to a point of time, at which sufficiently much entropy has been collected. Thus /dev/random
and MCRYPT_DEV_RANDOM
effectively collects randomness.
If you need to do strong crypto operations, use
MCRYPT_DEV_RANDOM
or/dev/random
.
Upvotes: 5
Reputation: 128
If you are using PHP >= 5.3.7, you should use the password_comp library which was written by guys involved in PHP and it utilizes BCRYPT which is the strongest algorithm available for PHP as of yet.
It's very simple and easy to use.
https://github.com/ircmaxell/password_compat
Hash the password simply
$hash = password_hash($password, PASSWORD_BCRYPT);
Verify the given password against the stored password hash
if (password_verify($password, $hash)) {
/* Valid */
} else {
/* Invalid */
}
Pretty simple stuff, like they say no need to rewrite was has already been written especially when it comes to security and written by the experts.
Upvotes: 2
Reputation: 830
You shouldn't rely on two-way encryption for passwords. If someone can get the cyphertext, they can usually also get the key to decrypt them.
Instead, you should use hashing or key derivation like blowfish or pbkdf2, those are one-way functions that are designed to be hard to crack.
Please, never ever encrypt passwords this way.
Upvotes: 2