Course contentsShow
← Node.js
Lesson 998 of 1,9809. Authentication and Identity ManagementFree lesson

Mitigating Enumeration Attacks

Standardize error messages across login and registration to hide valid user email detection.

In this lesson, you will learn how to prevent attackers from discovering which users exist in your database by standardizing your authentication feedback.

The Enumeration Problem

An enumeration attack occurs when an application provides different responses depending on whether a user account exists. For example, if your login endpoint says "User not found" for invalid emails but "Incorrect password" for existing emails, an attacker can script a request loop against your server to harvest a list of valid accounts. Once they have a list of confirmed emails, they can launch targeted phishing or brute-force attacks.

Standardizing Responses

To mitigate this, your application must provide the exact same response—and take the same amount of processing time—regardless of whether the input matches a record in your database.

Consider this insecure implementation:

// INSECURE: Reveals if the email exists
app.post('/login', async (req, res) => {
  const user = await db.users.findUnique({ where: { email: req.body.email } });
  
  if (!user) {
    return res.status(401).json({ message: "Email not found" });
  }
  
  if (!verifyPassword(req.body.password, user.password)) {
    return res.status(401).json({ message: "Incorrect password" });
  }
  
  res.json({ success: true });
});

To secure this, you should return a generic message like "Invalid email or password" regardless of which condition failed.

Preventing Timing Attacks

Even with generic messages, attackers can sometimes perform "timing attacks." If your database lookup for an email is significantly faster than the cryptographic work required to verify a password, the response time can reveal if the user was found.

A simple mitigation is to perform a "dummy" password check even when the user is not found:

// SECURE: Standardized message and uniform timing
app.post('/login', async (req, res) => {
  const user = await db.users.findUnique({ where: { email: req.body.email } });
  const genericError = "Invalid email or password";

  if (!user) {
    // Perform a dummy hash comparison to keep response times consistent
    await dummyHashComparison();
    return res.status(401).json({ message: genericError });
  }

  if (!await verifyPassword(req.body.password, user.password)) {
    return res.status(401).json({ message: genericError });
  }

  res.json({ success: true });
});

Key Takeaway: Always return uniform error messages and ensure consistent response times for all login and registration attempts to prevent attackers from leaking your user database contents.