What is an alternative for bcrypt to use with node?
Asked Answered
C

6

38

I have tried for days to get bcrypt installed on my windows machine with no luck. One of the dependencies (Windows 7 SDK) does not want to be installed even though I have tried numerous suggestions from around the net it just refuses to cooperate.

I need a good alternative to bcrypt which does not have any dependencies.

Claycomb answered 6/11, 2013 at 20:55 Comment(2)
What's wrong with node's built-in crypto functions?Lemmon
@Lemmon I don't have enough experience with encryption to answer that. But I've noticed that most projects and examples use bcrypt which seems odd if there are other solutions (mentioned below) that have zero dependencies.Claycomb
S
30

Check out https://npmjs.org/package/bcryptjs, it's fully compatible with bcrypt just without the dependencies.

Or https://npmjs.org/package/simplecrypt if you don't want the crypto boilerplate and just need to encrypt and decrypt strings.

Sexpot answered 6/11, 2013 at 21:24 Comment(3)
I will likely use one of these but Im not sure what you mean by boilerplate. Are you referring to the fact that it makes some assumptions? Like how many bits the key should be, encryption type etc? Other than that are the two different in any other way in terms of being easier or harder to circumvent?Claycomb
Right, the simplecrypt is really good at just doing simple string encodes and decodes without any configuration of salts, hashes, etc. bcryptjs has enterprise support from other modules such as npmjs.org/package/loopback, so I would definitely pick that over simplecrypt for anything more complex.Sexpot
bcryptjs does it have same security features as bcrypt?Scabrous
F
26

As of 24th April 2020 There is a nice built in way of hashing passwords using scrypt in the crypto module

// Using the built in crypto module

const { scryptSync, randomBytes } = require("crypto");


// Any random string here (ideally should be atleast 16 bytes)

const salt = randomBytes(16).toString("hex")


// Pass the password string and get hashed password back
// ( and store only the hashed string along with the salt in your database)
// {hashedPassword}${salt}

const getHash = (password) => scryptSync(password, salt, 32).toString("hex");

Flemish answered 24/4, 2020 at 9:30 Comment(5)
How to decrypt the same?Creative
You cannot decrypt it. its a one way hash. to compare the password simply hash the user entered text and compare it with the value stored with you.Flemish
i try to compare password for authentication. i tried what you said to hash the user input password and compare the hash result. but the result are differentVesuvius
If you don't store the salt, then how would you ever verify the password later?Moise
@Moise I corrected my statement by updating the comment.Flemish
H
12

Here is an improved version of @malik-bagwala with JsDocs, types and a match password function.

import { randomBytes, scryptSync } from 'crypto';

// Pass the password string and get hashed password back
// ( and store only the hashed string in your database)
const encryptPassword = (password: string, salt: string) => {
  return scryptSync(password, salt, 32).toString('hex');
};

/**
 * Hash password with random salt
 * @return {string} password hash followed by salt
 *  XXXX till 64 XXXX till 32
 *
 */
export const hashPassword = (password: string): string => {
  // Any random string here (ideally should be at least 16 bytes)
  const salt = randomBytes(16).toString('hex');
  return encryptPassword(password, salt) + salt;
};

// fetch the user from your db and then use this function

/**
 * Match password against the stored hash
 */
export const matchPassword = (password: string, hash: string): Boolean => {
  // extract salt from the hashed string
  // our hex password length is 32*2 = 64
  const salt = hash.slice(64);
  const originalPassHash = hash.slice(0, 64);
  const currentPassHash = encryptPassword(password, salt);
  return originalPassHash === currentPassHash;
};

Hostage answered 8/1, 2022 at 9:39 Comment(4)
Use crypto.timingSafeEqual instead of originalPassHash === currentPassHash for comparing both hashes to prevent timing attacks. You can find an implementation in this SO answerJemina
Love this! Nice way of salting the password by appending the salt to the salted hash. Genius! Unless someone finds out the algorithm :DExistential
@Jemina I don't think crypto.timingSafeEqual is really necessary, as it prevents indeed timing attacks that would let an attacker guess the value: as they are hashed, I don't think there is much an attacker can do with these.Dumpish
@Zitoun, indeed, you are right. I learned a bit later that value1 === value2 is an issue, while value1Hashed === value2Hashed is a primitive timing safe comparison. Here's an artcile about it: Double HMAC Comparison (Note: I'm aware password hashing != HMAC; however, both use hashing algorithms)Jemina
R
3

If someone faces similar issue, you can try bcryptjs which is optimized bcrypt written in JavaScript with zero dependencies and is also compatible to the C++ bcrypt.

Rhinitis answered 19/4, 2019 at 23:6 Comment(5)
Thank you! Helped. I was facing issues due to a build of bcrypt not existing for arm64-linux (was building a docker image using Alpine Linux on a M1 Mac)Karlotte
"Optimized" - 30% slower than bcrypt.Jemina
@Advena, slower is better when it comes to hash functions right?Blotch
@MatJ That's good question! When you verify passwords from db hash you may want to make it a bit slower. But what about when you hash them to save them into db? That 30% slower can be annoying.Rhinitis
@MatJ exactly as Nux said. Bcrypt is by design slow - no need to make it additionally slow by rewriting it in a high programming language (JavaScript).Jemina
L
0

You should really use the built-in crypto module for your encryption needs. It's basically a binding to OpenSSL, a fast, stable, secure, and well-vetted crypto library. Trying to implement your own crypto (or use someone else's unvalidated attempt at implementing crypto) is a recipe for disaster.

If you're looking to encrypt data, all you have to do is call crypto.createCipher, which returns a readable/writable Stream. Write data into the stream and it will emit data events with the encrypted data.

For example:

var stream = crypto.createCipher('aes192', 'mysecretpassword');
stream.on('data', function(enc) {
    // enc is a `Buffer` with a chunk of encrypted data
});

stream.write('some secret data');
stream.end();
Lemmon answered 6/11, 2013 at 23:36 Comment(1)
If you're storing passwords, this is not recommended. You need a salt or else all the passwords in your database could be compromised, e.g., with a rainbow table. Just a thought. Cheers!Mitis
M
0

It's possible to hash using either Argon2i, Argon2d or Argon2id (default), and verify if a password matches a hash.

https://www.npmjs.com/package/argon2

const password = 'password123';

async function hashPassword(password) {
  try {
    const hashedPassword = await argon2.hash(password);
    console.log('Hashed password:', hashedPassword);
  } catch (error) {
    console.error('Error hashing password:', error);
  }
}

hashPassword(password);
Mansuetude answered 2/6, 2023 at 16:34 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.