Reputation: 1320
I am working on a project which needs a very advanced security system when it comes to saving passwords etc. So my question is, is this way to save passwords safe enough?
This is the way I programmed:
When a user tries to log in, I first check if the username exists. If it does, I check if the password is correct using the check() function.
I use this Bcrypt class to encrypt and check.
Can you guys tell me if this way of encrypting and verifying is well enough for a big project?
Regards,
Jan Willem
Upvotes: 1
Views: 4631
Reputation: 1320
At first I want to thank you guys for the quick response, it is very important to me to make the system as hard to hack as possible.
Based on your recommendations I made the following system:
When a user registers, an unique hash is being created:
$unique_hash = hash('sha512', some defined security key (in the config) . openssl_random_pseudo_bytes(50));
At second, a salt is created:
$salt = strtr(base64_encode(openssl_random_pseudo_bytes(50)), '+', '.');
In another table, located in another database, this two variables are being combined and only the $unique_hash is stored in the users row in the users table.
Then we create the encrypted password, I use this function:
<?php
const COUNT = 8192;
const KEY_LENGTH = 254;
const ALGORITHM = 'sha512';
public static function encrypt($password, $salt, $algorithm = PBKDF2::ALGORITHM, $count = PBKDF2::COUNT, $key_length = PBKDF2::KEY_LENGTH, $raw_output = false) {
$algorithm = strtolower($algorithm);
if(!in_array($algorithm, hash_algos(), true))
die('PBKDF2 ERROR: Invalid hash algorithm.');
if($count <= 0 || $key_length <= 0)
die('PBKDF2 ERROR: Invalid parameters.');
$hash_length = strlen(hash($algorithm, "", true));
$block_count = ceil($key_length / $hash_length);
$output = "";
for($i = 1; $i <= $block_count; $i++)
{
$last = $salt . pack("N", $i);
$last = $xorsum = hash_hmac($algorithm, $last, $password, true);
for ($j = 1; $j < $count; $j++)
$xorsum ^= ($last = hash_hmac($algorithm, $last, $password, true));
$output .= $xorsum;
}
if($raw_output)
return substr($output, 0, $key_length);
else
return bin2hex(substr($output, 0, $key_length));
}
?>
Then we store this password in the default user table.
This will be it. Is this safe enough?
Jan Willem
Upvotes: 0
Reputation: 592
First, don't use bcrypt
. Use PBKDF2 instead. This will allow you to increase the difficulty of doing an offline brute force attack. [Not saying that bcrypt won't, they are essentially the same, but PBKDF2 supports any hashing function, not just Blowfish]
Second, here is how you should do this.
PBKDF2
function.Now I want to point out a timing attack in your system. If you tell an attacker that a username doesn't exist or the amount of time it takes to return an error is different (and shorter) than it takes to return a success, then you have a possibility for a side channel attack. Additional information can make it easier for an attacker to enumerate user accounts on your system (and if the username is based on Email, then they can now do a phishing attack against the user directly).
Upvotes: 0
Reputation: 522510
You should store the entire result in the database (including $2a$10
). If you're doing it properly, that hash is virtually impossible to brute force as is. Omitting that piece of information only makes your job more difficult, it doesn't make it meaningfully more difficult for a potential attacker.
Storing that value lets you see what algorithm the hash was created with and upgrade it over time. As hardware becomes faster you should increase the work factor of the algorithm and as better algorithms become available (hello scrypt!) you should upgrade the algorithm used. The way that works is that you check whether the hash was created using the latest and greatest when a user logs in. At that point is your opportunity to rehash the cleartext password and upgrade it.
Use PHP's new password_hash
API or the password_compat shim for older versions, those are well tested and perfectly "advanced".
Upvotes: 3