The problem is logical and predictable by the behavior of Bcrypt. I will try to pass a basic of the behavior of Bcrypt.
But, see that do this:
<?php
$senha = "a-mesma-senha";
echo password_hash($senha,PASSWORD_DEFAULT);
Will return:
$2y$10$l1Ka5O0C9oYxyY./MvA5YOH1JBTjcY9qTefOLUrZMZ8R4H8kR3UmO
But the same code will return:
$2y$10$J.eVvzGeh4FQh9uHb8Exz.U9jfn8hJhXs58bvdBYITkLfjLAJp0aK
And without changing anything also returns:
$2y$10$VFXgZFjjRLqeQvGIdeAtiuQR8jt1x.6wiz/L4dSBWC5w8fUbWwgDu
See for yourself, click on "Execute Code" several times and always the result will be different
This is self-explanatory. When you do the INSERT
you insert for example:
$2y$10$l1Ka5O0C9oYxyY./MvA5YOH1JBTjcY9qTefOLUrZMZ8R4H8kR3UmO
But when you do it again you try to search for, in the SELECT
:
$2y$10$VFXgZFjjRLqeQvGIdeAtiuQR8jt1x.6wiz/L4dSBWC5w8fUbWwgDu
Who are obviously different and you won’t find them.
This set of codes with $
in between represent:
$<algo>$<dificuldade>$<sal (22 caracteres)><senha (31 caracteres)>
Only the title of curiosity and so that you knew how this works note that if you do this:
// Não utilize isto em produção!
// Gera o BCrypt
$hashUm = password_hash('senha', PASSWORD_DEFAULT);
// Obtem o salt da hash gerada (primeiros 22 caracteres após o último $):
$salt = substr(substr($hashUm, strrpos($hashUm, '$')), 1, 22);
// Gera um nova senha COM O MESMO SALT:
$hashDois = password_hash('senha', PASSWORD_DEFAULT, ['salt' => $salt]);
if($hashDois === $hashUm){
echo 'Funcionou';
}
Do not use the above code for obvious security reasons.
Why does the above code work even generating twice? Because it is the same salt (the same difficulty and the same algorithm), so the same hash will result, this is like the verify_password
works.
This is "exactly" what happens when using the password_verify()
, it gets salt from the hash and then encrypts the input with the same salt and checks, but logically with side-Channel protections and the like.
The difference between Bcrypt/PBKDF2/Argon2i/Scrypt (...) against SHA256, MD5, SHA-1 (...) is precisely that they all have difficulty adjustment and also a salt, the salt in specific makes that identical passwords are not equal, see this publication also, as you can easily see above.
How to solve the problem:
Simple, first a value should be fixed for both sides, it should be "plain text", unencrypted.
For example:
$tokenPlanoComum = unpack('H*', random_bytes(128))[1];
$tokenSecretoCliente = unpack('H*', random_bytes(32))[1];
$tokenSecretoBanco = password_hash($tokenSecretoCliente,PASSWORD_DEFAULT);
You will save in the database the following information:
$tokenSecretoBanco
$tokenPlanoComum
You will send the customer the information:
$tokenPlanoComum
$tokenSecretoCliente
Then to give the SELECT
you will get via the $tokenPlanoComum
, in this way, for example:
SELECT * FROM tabela WHERE tokenPlanoComum = "$tokenPlanoComum"
Then after SELECT compare the information that the client sent (from the URL) to the database:
if(password_verify($_GET['tokenSecretoCliente'], $tabela['tokenSecretoBanco'])){
// Tudo certo!
}
Remember, if applicable, to set an expiration time to prevent you from using the same token after long time.
Because this is efficient?
If there is a fault in your system that allows you to get the information from the table (example a Read-Only SQL Injection), one of them is encrypted and only the client has the possession, so it will still be safe since it will need both correct. ;)
Because this works?
Because there is a common ground where it is immutable and will always be the same for both sides.
Place these two lines at the beginning of the script:
ini_set('dispaly_errors', true);error_reporting(E_ALL);
see if there are any errors.– rray
There is no error. What exists is a mistaken use of cryptography. The
password_hash
until version 7.1 is only Bcrypt. It generates a different (and theoretically unique) salt for each time. So when you use it to give theINSERT
he will save$(algo)$(dificuldate)$(salt [primerios 16 caracteres, salvo engano])(hash da senha)
. Hence theverify_password
works, because it generates the input password again with the same salt and then compares. However do what you are doing, using aSELECT
is impossible because the new$email
will be different from$email
that is there– Inkeliz
but PHP does not present any error, the HTML code simply does not appear!
– Diana Madeira
the page appears blank
– Diana Madeira
Appears blank because it does not find SELECT less (or more) than a line. Create a
else
in theif(mysqli_num_rows($sql)==1)
and you’ll see this. Of avar_dump(mysqli_num_rows($sql))
;– Inkeliz
Is passing key and reset to url?
– Maurivan
Inkeliz did what you said and appeared the following: int(0)
– Diana Madeira
Maurivan this is my link: $link="<a href='http://unn-w17015779.newnumyspace.co.uk/reset.php?key=". $email." &reset=". $pass." '>Click To Reset password</a>";
– Diana Madeira
@Dianamadeira Precisely, he does not find because Bcrypt always generates a different hash, if it always generates something different as you want to think what is equal to it?! I’ll post an answer explaining that.
– Inkeliz
ok hold on Inkeliz
– Diana Madeira