How Can QA Check the Login from a Security Lens?

Your login system is the front door to your application. As QA, it’s not enough to verify whether users can log in with the correct credentials. You also need to check how secure that process really is. A weak login leaves the entire system exposed, even if functional tests pass.

In this post, we’ll compare a bad (insecure) login flow with a good (secure) login flow, and outline what QA engineers should validate to ensure the login process can withstand real-world attacks.

Bad Login Code (What QA Should Watch Out For)

app.post("/login", async (req, res) => {
  const { email, password } = req.body;

  // ❌ Vulnerable to SQL Injection
  const [rows] = await db.query(
    `SELECT * FROM users WHERE email='${email}' AND password='${password}'`
  );

  if (rows.length > 0) {
    // ❌ Passwords stored in plain text
    // ❌ No session or token security
    res.send("Login successful");
  } else {
    res.send("Invalid credentials");
  }
});

QA Security Concerns:

  • Passwords stored in plain text.
  • Vulnerable to SQL Injection (try ' OR '1'='1 as a test).
  • No brute-force protection (check if unlimited wrong attempts are allowed).
  • No secure session/token handling.
  • No CSRF or XSS protection.

Good Login Code (Hardened and Secure)

app.post("/login", async (req, res) => {
const { email, password } = req.body;

  try {
    // ✅ Prevent SQL Injection
    const [rows] = await db.execute(
      "SELECT id, password_hash FROM users WHERE email = ?",
      [email]
    );

    if (rows.length === 0) return res.send("Invalid credentials");

    const user = rows[0];

    // ✅ Compare with bcrypt hash
    const isMatch = await bcrypt.compare(password, user.password_hash);
    if (!isMatch) return res.send("Invalid credentials");

    // ✅ Issue JWT with secure cookie
    const token = jwt.sign({ userId: user.id }, process.env.JWT_SECRET, {
      expiresIn: "1h",
    });

    res.cookie("auth_token", token, {
      httpOnly: true,
      secure: true,
      sameSite: "Strict",
      maxAge: 3600000,
    });

    res.send("Login successful");
  } catch (err) {
    console.error(err);
    res.status(500).send("Internal server error");
  }
});

QA Security Checks

  • Passwords are hashed (never visible in DB).
  • Queries are parameterized (try SQL injection payloads and confirm they fail).
  • Authentication uses JWT/session cookies with HttpOnly, Secure, SameSite=Strict.
  • Ensure rate limiting is enforced (after multiple failed attempts, access is blocked).
  • Test for CSRF & XSS vulnerabilities (malicious scripts or cross-site requests should be blocked).

What QA Should Validate Beyond Functional Testing?

Password Security

  • Ensure strong password policies (length, complexity, no common passwords).
  • Check if passwords are hashed in DB (request DB dump during security review).

Database Query Safety

  • Run SQL injection attempts and verify the system rejects them.
  • Use automated tools (e.g., SQLMap) for penetration-style testing.

Authentication Tokens

  • Verify cookies have HttpOnly, Secure, and SameSite flags.
  • Test session expiry and token invalidation after logout.

Brute Force Protection

  • Try multiple wrong logins—system should lock or delay further attempts.
  • Ensure rate-limiting error messages are consistent (no leaking valid/invalid user info).

CSRF & XSS Defense

  • Check if CSRF tokens exist on login forms.
  • Validate inputs are sanitized (attempt and confirm it’s neutralized).

Error Handling

  • Errors should be generic (avoid leaking database details or stack traces).

Conclusion

As QA engineers, looking at login through a security lens means going beyond “does it work” to asking “is it safe?”. Bad logins may pass functional tests but fail security checks. Good logins ensure password safety, query protection, secure tokens, brute-force defense, and CSRF/XSS resilience.

In your test strategy, always include negative testing and attack simulations. Remember: login is not just a feature, it’s your first line of defense.