You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

MEAN栈密码比对异常:bcrypt.compareSync返回false的解决方法及替代方案

Hey there, let's figure out why your bcrypt password comparison keeps returning false, and walk through both fixes for the bcrypt approach and alternative solutions if you'd rather skip using bcrypt entirely.

First: Fixing the Bcrypt Issue

Most of the time, this false result comes from a mistake in how you're generating or storing the password hash during registration. Let's break down the key checks and fixes:

1. Verify Your Registration Hash Logic

Your provided register route uses a GET method, which is unusual for registration—you should be using a POST to handle password submission. Let's start with the correct registration flow, since a broken hash here guarantees login failure:

Correct Registration Example

router.route('/register').post(async (req, res) => {
  try {
    // Generate a hash with a valid salt round (10-12 is standard)
    const passwordHash = bcrypt.hashSync(req.body.password, 10);
    
    // Save ONLY the hash to your database (never plaintext!)
    const newUser = await User.create({
      username: req.body.username, // or whatever fields you need
      hash: passwordHash // Make sure this field name matches what you use in login
    });
    
    res.status(201).json(newUser);
  } catch (err) {
    res.status(500).json(err);
  }
});

Common Registration Mistakes to Check:

  • Storing plaintext instead of hash: Double-check your database to confirm the hash field contains a 60-character bcrypt string (not the raw password).
  • Truncated hash: Ensure your database's hash field has enough length (use VARCHAR(255) at minimum)—bcrypt hashes are ~60 chars, so shorter fields will cut off part of the hash.
  • Async/sync mismatch: If you used bcrypt.hash() (async) instead of hashSync() but didn't await the promise, you might have stored a Promise object instead of the actual hash. Always await async bcrypt calls:
    // Correct async registration
    const passwordHash = await bcrypt.hash(req.body.password, 10);
    

2. Debug Your Login Comparison

Add debug logs to confirm you're comparing the right values:

router.route('/login').post(async (req, res) => {
  try {
    const user = await User.findOne({ where: { username: req.body.username } });
    if (!user) return res.status(401).json("User not found");

    // Add these logs to verify values
    console.log("Submitted password:", req.body.password);
    console.log("Stored hash:", user.hash);
    
    const isMatch = bcrypt.compareSync(req.body.password, user.hash);
    console.log("Comparison result:", isMatch);

    if (!isMatch) return res.status(401).json("Incorrect password");

    // Generate JWT as usual
    const token = jwt.sign({ id: user.id }, process.env.JWT_SECRET, { expiresIn: "1d" });
    res.status(200).json({ token, user });
  } catch (err) {
    res.status(500).json(err);
  }
});
  • If the stored hash doesn't match what was generated during registration, your database save step is broken.
  • If the submitted password isn't the raw plaintext (e.g., frontend encrypted it), that's a problem—bcrypt needs the raw password to compare.
Alternative: No Bcrypt? Use Node.js Crypto Module

If you want to avoid bcrypt, you can use Node.js's built-in crypto module with the PBKDF2 algorithm (a secure, slow-hash method for passwords). Here's how to implement it:

Registration with Crypto/PBKDF2

const crypto = require('crypto');

router.route('/register').post(async (req, res) => {
  try {
    // Generate a unique random salt for each user
    const salt = crypto.randomBytes(16).toString('hex');
    // Generate hash with high iteration count (100,000+ is recommended)
    const passwordHash = crypto.pbkdf2Sync(
      req.body.password,
      salt,
      100000,
      64,
      'sha256'
    ).toString('hex');

    // Save both hash and salt to the database (you need the salt for login!)
    const newUser = await User.create({
      username: req.body.username,
      passwordHash: passwordHash,
      salt: salt
    });

    res.status(201).json(newUser);
  } catch (err) {
    res.status(500).json(err);
  }
});

Login with Crypto/PBKDF2

router.route('/login').post(async (req, res) => {
  try {
    const user = await User.findOne({ where: { username: req.body.username } });
    if (!user) return res.status(401).json("User not found");

    // Re-generate the hash using the stored salt
    const generatedHash = crypto.pbkdf2Sync(
      req.body.password,
      user.salt,
      100000,
      64,
      'sha256'
    ).toString('hex');

    // Compare the generated hash to the stored one
    const isMatch = generatedHash === user.passwordHash;
    if (!isMatch) return res.status(401).json("Incorrect password");

    // Generate JWT
    const token = jwt.sign({ id: user.id }, process.env.JWT_SECRET, { expiresIn: "1d" });
    res.status(200).json({ token, user });
  } catch (err) {
    res.status(500).json(err);
  }
});

Notes for the Crypto Approach:

  • Don't skimp on iterations: Higher iteration counts make brute-force attacks harder—100,000 is a good starting point.
  • Always store unique salts: Never reuse salts across users; this prevents rainbow table attacks.
  • While PBKDF2 is secure, bcrypt is purpose-built for password storage with adaptive cost scaling (it gets slower as hardware improves), so bcrypt is still the recommended choice if you can get it working.

内容的提问来源于stack exchange,提问作者Casey Cao

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.26 09:18:29