2

Following this tutorial, I got a bunch of questions with this code :

const crypto = require("crypto")

async function hash(password) {
    return new Promise((resolve, reject) => {
        // generate random 16 bytes long salt
        const salt = crypto.randomBytes(16).toString("hex")

        crypto.scrypt(password, salt, 64, (err, derivedKey) => {
            if (err) reject(err);
            resolve(salt + ":" + derivedKey.toString('hex'))
        });
    })
}

Is it worth or relevant to:

  • encrypt my salt before saving it to the database (with a key I have for example in a .env file to decrypt it when verifying password)
  • concat a random key (another one that I keep in a .env also I guess) to the salt (but I don't add it in the db) and use it for verification also.

For example, the code above will become something like this:

const crypto = require("crypto")

async function hash(password) {
    return new Promise((resolve, reject) => {
        // generate random 16 bytes long salt
        const generatedSalt = crypto.randomBytes(16).toString("hex")
        const salt = `${generatedSalt}${process.env.BASE_SALT}`

        crypto.scrypt(password, salt, 64, (err, derivedKey) => {
            if (err) reject(err);
            const encryptedSalt = someEncryptionfunction(generatedSalt,process.env.SALT_ENCRYPT_KEY)
            resolve(encryptedSalt + ":" + derivedKey.toString('hex'))
        });
    })
}

And the verification function will be like:

async function verify(password, hash) {
    return new Promise((resolve, reject) => {
        const [encryptedSalt, key] = hash.split(":")
        const generatedSalt = someDecryptionfunction(encryptedSalt,process.env.SALT_ENCRYPT_KEY)
        const salt = `${generatedSalt}${process.env.BASE_SALT}`
        crypto.scrypt(password, salt, 64, (err, derivedKey) => {
            if (err) reject(err);
            resolve(key == derivedKey.toString('hex'))
        });
    })
}
3
  • Why do you want to do this? What effect are you hoping to have?
    – schroeder
    Commented Jun 15 at 17:13
  • I'm was just wondering if it adds any security improvement or totaly worthless. Commented Jun 15 at 17:33
  • I think the answer is self-evident once you've understood what the salt in a password hash is for and realize based on this understanding that there is no need to protect it. Commented Jun 15 at 18:05

1 Answer 1

1

There's absolutely no point in encrypting the salt. The salt isn't secret, it just has to be globally unique and unpredictable -- which is already the case when you use random bytes.

Storing the plaintext salt next to the hash is perfectly fine. In fact, algorithms like bcrypt and the newer Argon2 have a specific output format which includes all input parameters (salt, iterations etc.):

$argon2i$v=19$m=65536,t=2,p=4$c29tZXNhbHQ$RdescudvJCsgt3ub+b+dWRWJTmaaJObG

Here, c29tZXNhbHQ (base64-encoded string somesalt) is the salt, separated with a $ sign from the hash.

For scrypt, you could use the generic Password Hashing Competition string format.

17
  • 1
    Wrong. Salt doesn't need to be unpredictable. Its purpose is to prevent rainbow tables. Salt just should not be reused.
    – mentallurg
    Commented Jun 16 at 10:25
  • @mentallurg: If you don't believe me, then create your own question and let others explain whether or not salts have to be unpredictable.
    – Ja1024
    Commented Jun 16 at 11:14
  • 1
    "If the salt is predictable, then an attacker can build lookup tables in advance" - It is absolute nonsense. 🙂 You don't understand why rainbow tables are used. Let me help you. Briefly: Rainbow tables take a lot of storage and a lot of computational resources. When the same salt is used for all passwords in a database, this allows to re-use the same rainbow table for every hash. Where as if every hash was computed using a different salt, the rainbow table cannot be re-used, and for each hash a separate rainbow table is needed. This makes rainbow tables impractical.
    – mentallurg
    Commented Jun 16 at 23:14
  • @mentallurg: It's interesting how you absolutely refuse to let other members of this community fact-check your claims. As I've said, create your own question instead of making the same incorrect comments over and over again. You could also simply read the questions that have been linked above. They talk about why salts have to be unpredictable in great lengths.
    – Ja1024
    Commented Jun 17 at 9:28
  • Like salts, lookup tables have multiple purposes. This may sound baffling to you, but that’s how it is. One obvious purpose is to reuse hash calculations. This can indeed be prevented with unique salts (note that salts have to be globally unique, not just unique within one application). However, there’s a second purpose of lookup tables: If the attacker knows what the salts of future user accounts will be, then they can calculate the corresponding hashes in advance. Do you understand the idea? The attacker can speed up future brute-force attacks by investing computing power now.
    – Ja1024
    Commented Jun 17 at 9:51

Not the answer you're looking for? Browse other questions tagged .