The hashPassword
includes the salt used. I practically answered this in another matter, but since the question wasn’t specifically about that, then I guess I can answer it here too.
The same way that "works on Node" is the same as it works anywhere, since it implements the same Bcrypt.
Bcrypt is deterministic. For this reason it will have the same result if using the same salt as any existing KDF. Bcrypt has three parameters:
- Computational cost.
- Salt.
- Password.
There are other derivative functions (Kdfs) that have other input parameters. Argon2, for example, has individual cost parameters (for memory, iteration, etc.), output length parameters. On the other hand, HKDF does not have computational cost, because it is not intended for passwords, but has hash length, result length, flexible.
As a result will:
However, the first two values are public, in the case of Bcrypt. Therefore, Bcrypt itself uses a format, similar to Modular Crypt Format, where:
${Algoritmo}${Custo}${Salt}{Hash}
This way the hash is included in values that make it possible to compare the same hash, since we know the algorithm, the cost, the salt and the expected result.
A small test, with a result of:
$2b$10$111111111111111111111u1Fg3CCqE4CYDkm0w0C9gxJ.HpXSNHlS
You can get the algorithm, the cost, the salt and the "expected hash", therefore:
let resultado = '$2b$10$111111111111111111111u1Fg3CCqE4CYDkm0w0C9gxJ.HpXSNHlS';
let algo = resultado.substring(1, 3);
let custo = resultado.substring(4, 6);
let salt = resultado.substring(7, 29);
So just do the bcrypt.hash
and you can compare the value, normally:
const crypto = require('crypto');
const bcrypt = require('bcrypt');
async function init(plainPassword) {
let resultado = '$2b$10$111111111111111111111u1Fg3CCqE4CYDkm0w0C9gxJ.HpXSNHlS';
let algo = resultado.substring(1, 3);
let custo = resultado.substring(4, 6);
let salt = resultado.substring(7, 29);
let hash = await bcrypt.hash(plainPassword, '$' + algo + '$' + custo + '$' + salt);
console.log(crypto.timingSafeEqual(Buffer.from(hash), Buffer.from(resultado)));
}
init('cba');
See that there is none bcrypt.compare
, the crypto.timingSafeEqual
does the same as the ===
, however it is safe against timing-Attack. That’s basically what the bcrypt.compare
makes, when you include the hashPassword
he already has the necessary data. :)
So, in this case, what is the function of
salt
?– Costamilam
@Guilhermecostamilam Prevent the same password from having the same result. If you have KDF(1,1), KDF(1,2), KDF(1,3) you will get different results, even if "1" is in both. This prevents pre-computation attacks, since to pre-compute the result of an X password you will have to compute all possibilities of SALT with X.
– Inkeliz
I was thinking about doing something like
bcrypt.hash(plainPassword, salt+systemSalt)
beingsystemSalt
a constant salt in the system, makes a difference? Orbcrypt.hash(salt+plainPassword+systemSalt, salt)
? Some of them can increase security?– Costamilam
This is called "Pepper". There are no significant gains in security, in Bcrypt this is worse, because salt is limited to 16 bytes. In other KDF (Argon2) you can have larger salt, so you can still have 32 bytes of salt and 32 bytes of Pepper, for example. You put a salt next to the password is a complete catastrophe. Bcrypt only supports passwords up to 72 bytes, so using 16 bytes will reduce the possibility to a maximum of 56 bytes. If you are still adding as a prefix, depending on the size, you will end up disabling the password. Use the algorithm as it was proposed, or use another algorithm.
– Inkeliz
@Guilhermecostamilam The "Pepper" makes sense if you are physically isolated, for example a database on one server, the web on another and perhaps another HSM. This way compromising one will not give all the information, but in Bcrypt this is more complicated, for lack of space.
– Inkeliz
A doubt, the
saltRouds
, the higher the number the longer the time/resources it will take to create the password, correct? But this increases the security to compensate? What would be more or less the ideal value that balances between the two?– Costamilam
Yes. This makes it harder to find the password through the hash, but easier to do a Dos, by using more server resources as well. The ideal depends on how often passwords are required and how long the response time is tolerated and also on the hardware. They usually recommend up to 1 second or up to 500ms. Set the highest cost where your server is able to meet within this time, usually anywhere from 14 to 17.
– Inkeliz