Upd. If you know what BCrypt is, you can stop reading. If you are using PHP 5.5+ you can read
this article . Below, I invented my own bicycle, a worker, but with two steering wheels, a rear spare. He was young, hot.
Hi, Habr! Today, in the process of developing an authentication system for my project, I had the choice of how to store user passwords in a database? A lot of options come to mind. The most obvious:
- Store passwords in the database in the clear.
- Use regular hash crc32, md5, sha1
- Use the crypt () function
- Use static “salt”, constructions of the form md5 (md5 ($ pass))
- Use a unique "salt" for each user.
The first option was dropped of course right away. The use of ordinary hashes after a brief deliberation also had to be discarded for several reasons.
')
Hash Collision
A hash collision occurs when it produces the same result on different input data. Of course, the probability of this is quite small, and depends on the length of the hash. However, the obsolete (but still sometimes used) function crc32 () returns a 32-bit integer as a hash. That is, in order to select a password for such a hash, according to the theory of probability, it is necessary to obtain 2 ^ 32 = 4,294,967,296 different hashes. Even on my free hosting, crc32 runs at about 350,000 times per second - calculate for yourself how many seconds it takes to crack such a hash;)
Of course, this does not apply to md5 () (128-bit hash) and especially sha1 () (160-bit hash). Using their collision is almost impossible, although
there is one little
article ...Rainbow tables
Rainbow tables consist of hashes of the most frequently used passwords — names, dates of birth, animal names, etc. These tables can include millions, billions of values, but working with them is relatively fast, and it’s easy to check the hash for one of the values. Partially, they can be protected from them with the help of “salt” or constructions of the md5 type (sha1 (md5 ($ pass))).
$password = "easypassword";
Rainbow tables. Part 2
Static salt and similar structures can serve well enough ... as long as the structure of these structures and the salt are kept secret. If the attacker finds out the secret of hashing, he can easily modify his “rainbow table” for him. And since we cannot absolutely rely on the security system of our server, we need to look for another option. One solution would be to generate a unique salt for each user, something like:
$hash = sha1($user_id . $password);
It is even better to generate a completely random salt, like this:
Of course, a unique salt will have to be added to the database, but even if it gains access to it, an attacker is unlikely to be able to generate several million rainbow tables.
Hash rate
It would seem - the sooner the better. The faster the hash is generated, the faster our user will be able to register and start already bringing profit. However, the higher the hash rate, the faster the hacker will be able to pick it up.
Modern PCs with powerful GPUs can count millions of hashes per second or more. And it allows you to break passwords by simple selection, using brute-force attacks. Do you think that a password of 8 characters is safe enough? If the password uses lower case and upper case characters and numbers, the total number of possible characters will be 62 (26 + 26 + 10). For a password of 8 characters, there are 62 ^ 8 different combinations (of the order of 218 trillion). With a speed of 1 billion hashes per second (small enough for a brute force attack), the password will be broken in about 60 hours. And for the most common password length of 6 characters, the decryption duration is less than two minutes.
You can of course neglect users who use short and simple passwords, or force everyone to voluntarily-compulsorily use 10-character passwords with punctuation marks and Sumerian cuneiform symbols. But it’s better to use slower hashing functions. For example, you can slow down the hash function manually 1000 times with the following code:
function myhash($password, $unique_salt) { $salt = "f#@V)Hu^%Hgfds"; $hash = sha1($unique_salt . $password);
Using it, instead of 60 hours, the hacker will break an 8-character password for about 7 years. A more convenient way to slow down is to use
the Blowfish algorithm implemented in PHP via crypt (). You can check the availability of this algorithm using if (CRYPT_BLOWFISH == 1) echo 'it works!'; PHP 5.3 Blowfish is already enabled.
function myhash($password, $unique_salt) {
$ 2a is an indication that the Blowfish algorithm will be used.
$ 10 is the power of slowing function. In this case, equal to 2 ^ 10. Can take values ​​from 04 to 31
We use it on a specific example:
$hash = '$2a$10$dfda807d832b094184faeu1elwhtR2Xhtuvs3R9J1nfRGBCudCCzC'; $password = "verysecret"; if (check_password($hash, $password)) { echo " !"; } else { echo " !"; } function check_password($hash, $password) {
Such a code should provide maximum security - it is almost impossible to pick up a password of normal complexity and length (programmatically, of course).