I am using bcrypt.js for basic login. I have found that the below code runs noticeably quicker when no user is found, since it exits immediately, and no check is done on the hash. This could give an attacker insight into whether a username exists in the database or not. Is there a way to mitigate this using bcrypt.js library?

For example, here are some times when testing.

User does not exist

POST /login 401 11.846 ms - 42

User exists, wrong password

POST /login 401 104.914 ms - 42

More information


router.post('/login', async (req, res) => {
    if (!req.body.email) {
        res.json({ success: false, message: "No email provided" });
    if (!req.body.password) {
        res.json({ success: false, message: "No password provided" });

    const user = await User.findOne({ email: req.body.email });
    // If no user found
    if (!user) {
        return res.status(401).json({ message: 'Invalid username or password' });
    bcrypt.compare(req.body.password, user.password, function (err, result) {
        if (err) {
            return res.render('login');
        if (result) {
            console.log("User authenticated successfully");
            return res.redirect('/');
        } else {
            return res.status(401).json({ message: 'Invalid username or password' });

1 Answer 1


The timing difference is between bcrypt.compare called in case of an existing user and not called when the user does not exist. To prevent this timing difference simply call bcrypt.compare also in the latter case. Since you cannot compare it to the real users password hash in this case just compare it to a fixed default hash.

  • 1
    Make sure the default hash you use has the same work factor as the real hashes (and that you update it when you increase the work factor of those in future).
    – Gh0stFish
    Sep 27, 2023 at 7:21

You must log in to answer this question.

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