Back to Web Vulnerabilities: SQLi, XSS, CSRF, SSRF
Q1Question 1 of 7

Broken Authentication and Best Practices

Key Concepts
  • Broken authentication is a class of weaknesses that let attackers compromise user identity through the login process itself.
  • Weak passwords are defended by enforcing minimum length, screening against breached-password lists, and hashing with bcrypt or Argon2.
  • Brute force and credential stuffing are defended by rate limiting, account lockout after repeated failures, and CAPTCHA.
  • Multi-factor authentication (MFA) ensures a stolen password alone is not enough for account access.
  • Session tokens must be high-entropy, HttpOnly, Secure, SameSite, rotated on login, and invalidated on logout.
  • Respond to login failures with uniform generic messages and timing to prevent username enumeration.

Definition

Protect the login itself: strong hashed passwords, lockout and rate limiting, MFA, and well-managed tokens.
  • Broken authentication – a class of weaknesses that let attackers compromise user identity through the login process.
  • Weak passwords – passwords that are too short, common, or stored with weak hashing (MD5, SHA-1, plaintext).
  • Missing lockout – no rate limiting or account lockout after repeated failures enables brute force and credential stuffing.
  • Missing MFA – without multi-factor authentication, a stolen password gives full access.
  • Poor token management – predictable session IDs, exposed tokens in URLs, no rotation on login, no invalidation on logout.
  • Username enumeration – different error messages or timing for valid vs invalid usernames reveals which accounts exist.

Broken authentication is like National Bank of India letting anyone guess a customer's PIN as many times as they want, accepting the PIN 1234, writing PINs on sticky notes, and never asking for a second proof like an OTP. An attacker simply keeps guessing or finds the sticky note and walks in. The fixes are a strong PIN rule, blocking the counter after a few wrong tries, keeping PINs locked away as scrambled hashes, and requiring a one-time code sent to the customer's phone for anything risky.

Understanding the Concepts

What is broken authentication?

Broken authentication is a family of weaknesses that let attackers take over user identities. It includes weak passwords, no account lockout, exposed session tokens, missing MFA, and username enumeration. In banking these are among the most dangerous flaws because compromising the login compromises the money.

TEXT
Common broken authentication weaknesses:
- Passwords stored in plaintext or with MD5/SHA-1
- No lockout: attacker tries 10,000 passwords per minute
- Credential stuffing: leaked passwords from other sites reused
- Session tokens in URL (/page?session=abc123)
- No MFA: one stolen password = full account access
- Different error for "wrong username" vs "wrong password" (enumeration)

How do you defend against weak passwords?

Enforce a sensible minimum length (at least 8-12 characters), screen against known-breached password lists (like the Have I Been Pwned list), and store passwords with a slow, salted, adaptive hash like bcrypt, scrypt, or Argon2. Never store plaintext or use fast hashes like MD5 or SHA-1.

JAVA
package com.bank.security;

import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

public class PasswordHashingDemo {

    private static final PasswordEncoder ENCODER = new BCryptPasswordEncoder(12);

    public static void main(String[] args) {
        String rawPassword = "Rajesh@SecurePass2024";

        // Hash on registration
        String hash = ENCODER.encode(rawPassword);
        System.out.println("Stored hash: " + hash);

        // Verify on login
        boolean matches = ENCODER.matches(rawPassword, hash);
        System.out.println("Password matches: " + matches);

        // Wrong password
        boolean wrong = ENCODER.matches("wrongpassword", hash);
        System.out.println("Wrong password matches: " + wrong);
    }
}
TEXT
Stored hash: $2a$12$kR7xP2qM9nTvWz3Y...bcrypt_hash
Password matches: true
Wrong password matches: false

How do you defend against brute force?

Rate limit login attempts and lock the account after a threshold of failures. Track failures per username and per IP, and alert on distributed attempts.

JAVA
package com.bank.security;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;

public class LoginRateLimiter {

    private static final int MAX_ATTEMPTS = 5;
    private final Map<String, AtomicInteger> attempts = new ConcurrentHashMap<>();

    public boolean isLocked(String username) {
        AtomicInteger count = attempts.get(username);
        return count != null && count.get() >= MAX_ATTEMPTS;
    }

    public void recordFailure(String username) {
        attempts.computeIfAbsent(username, k -> new AtomicInteger(0))
            .incrementAndGet();
        if (isLocked(username)) {
            System.out.println("Account " + username + " locked after "
                + MAX_ATTEMPTS + " failed attempts.");
        }
    }

    public void resetOnSuccess(String username) {
        attempts.remove(username);
    }
}
TEXT
Attempt 1: wrong password --> failure recorded (1/5)
Attempt 2: wrong password --> failure recorded (2/5)
...
Attempt 5: wrong password --> Account rajesh locked after 5 failed attempts.
Attempt 6: correct password --> 423 Locked (must wait or contact support)

What is MFA and why does it matter?

Multi-factor authentication adds a second factor: something you have (TOTP code, phone push) or something you are (biometric). A stolen password alone is not enough.

JAVA
package com.bank.security;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.nio.ByteBuffer;

public class TotpVerifier {

    public boolean verifyTotp(String secretKey, int userCode) {
        long timeStep = System.currentTimeMillis() / 30_000; // 30-second window
        int expected = generateTotp(secretKey, timeStep);
        int previous = generateTotp(secretKey, timeStep - 1); // allow clock skew

        boolean valid = (userCode == expected || userCode == previous);
        System.out.println(valid ? "MFA verified." : "Invalid MFA code.");
        return valid;
    }

    private int generateTotp(String key, long counter) {
        try {
            Mac mac = Mac.getInstance("HmacSHA1");
            mac.init(new SecretKeySpec(key.getBytes(), "HmacSHA1"));
            byte[] hash = mac.doFinal(ByteBuffer.allocate(8).putLong(counter).array());
            int offset = hash[hash.length - 1] & 0xF;
            int code = ((hash[offset] & 0x7F) << 24
                | (hash[offset + 1] & 0xFF) << 16
                | (hash[offset + 2] & 0xFF) << 8
                | (hash[offset + 3] & 0xFF)) % 1_000_000;
            return code;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}
TEXT
Login flow with MFA:
1. Rajesh enters username + password --> verified
2. Server prompts for TOTP code
3. Rajesh enters 6-digit code from authenticator app
4. Server verifies code --> MFA verified. Access granted.

Attacker with stolen password:
1. Enters username + password --> verified
2. Prompted for TOTP code
3. Does not have Rajesh's phone --> cannot produce code
4. Access denied.

How do you prevent username enumeration?

Return the same generic error message and use uniform response timing for both invalid usernames and wrong passwords. This prevents attackers from discovering which accounts exist.

JAVA
package com.bank.security;

public class LoginService {

    // WRONG: different messages reveal valid usernames
    public String loginWrong(String username, String password) {
        if (!userExists(username)) {
            return "Username not found."; // tells attacker this username is invalid
        }
        if (!checkPassword(username, password)) {
            return "Wrong password."; // tells attacker this username IS valid
        }
        return "Login successful.";
    }

    // CORRECT: uniform generic message
    public String loginCorrect(String username, String password) {
        // Always perform the same work regardless of whether username exists
        boolean valid = authenticateUser(username, password);
        if (!valid) {
            return "Invalid username or password."; // generic, no hint
        }
        return "Login successful.";
    }

    private boolean userExists(String u) { return true; }
    private boolean checkPassword(String u, String p) { return true; }
    private boolean authenticateUser(String u, String p) { return true; }
}

How do you manage session tokens securely?

Session tokens must be high-entropy (cryptographically random), transmitted only over TLS, stored in HttpOnly Secure SameSite cookies, rotated on login to prevent session fixation, and invalidated on logout and after inactivity.

JAVA
package com.bank.security;

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpSession;

public class SessionSecurityService {

    public void secureLogin(HttpServletRequest request, String authenticatedUser) {
        // Rotate session ID to prevent fixation
        request.getSession().invalidate();
        HttpSession newSession = request.getSession(true);
        newSession.setAttribute("user", authenticatedUser);
        newSession.setMaxInactiveInterval(1800); // 30 min inactivity timeout
        System.out.println("Session rotated. New ID: " + newSession.getId());
    }

    public void logout(HttpServletRequest request) {
        HttpSession session = request.getSession(false);
        if (session != null) {
            session.invalidate();
            System.out.println("Session invalidated on logout.");
        }
    }
}

What are common mistakes?

Storing passwords with MD5 or SHA-1 is a common mistake. These are fast hashes designed for integrity checking, not password storage. An attacker with a GPU can crack billions of MD5 hashes per second.

JAVA
// WRONG: fast hash (crackable in seconds)
import java.security.MessageDigest;
String hash = MessageDigest.getInstance("MD5").digest(password.getBytes());

// CORRECT: slow adaptive hash (resistant to brute force)
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
String hash = new BCryptPasswordEncoder(12).encode(password);

What should a senior engineer know?

Senior engineers prefer phishing-resistant factors like WebAuthn (FIDO2 hardware keys) over SMS-based OTP where possible, because SMS is vulnerable to SIM swapping. They implement step-up MFA for high-risk actions like adding a payee or making large transfers, even if the user already passed MFA at login.

Senior engineers also monitor for anomalies: logins from unusual locations, multiple accounts accessed from one IP, and sudden changes in login patterns that suggest credential stuffing campaigns.

Visual Overview

DIAGRAM
  Broken Authentication Attack Surface:
  ┌──────────────────────────────────────┐
  │         LOGIN ENDPOINT               │
  │                                      │
  │  Weak passwords ──► Brute force      │
  │  No lockout    ──► Credential stuff  │
  │  No MFA        ──► Single factor     │
  │  Enumeration   ──► Account discovery │
  └──────────────────────────────────────┘

  Defense Layers:
  ┌──────────────────────────────────────┐
  │ 1. Strong password + bcrypt hash     │
  │ 2. Rate limit + lockout              │
  │ 3. MFA (TOTP / WebAuthn)             │
  │ 4. Uniform error messages            │
  │ 5. Session rotation + HttpOnly       │
  │ 6. Anomaly monitoring                │
  └──────────────────────────────────────┘

Key Takeaway

Broken authentication is a family of weaknesses that let attackers take over user identities. It includes weak or reused passwords, no account lockout or rate limiting so brute force and credential stuffing succeed, exposed or predictable session tokens, and missing multi-factor authentication. In a bank these are among the most dangerous flaws because compromising the login compromises the money.

The best-practice baseline starts with passwords: enforce a sensible minimum length, screen against known-breached password lists, and store passwords with a slow adaptive hash like bcrypt or Argon2 with a suitable work factor, never plaintext or fast hashes. Then defend the login endpoint with rate limiting and lockout, so after a handful of failures the account is temporarily locked and distributed attempts are detected.

On top of that, multi-factor authentication ensures a stolen password alone is not enough, and step-up MFA is required for high-risk actions like adding a payee or making a large transfer. Login failures return a generic error message with uniform timing so attackers cannot enumerate valid usernames.

Finally, tokens are managed carefully: high-entropy session identifiers sent only over TLS, stored in HttpOnly Secure SameSite cookies, rotated on login to prevent fixation, and invalidated on logout and after inactivity. Together, hashing, lockout, MFA, and disciplined token handling turn a single leaked credential from a full account takeover into a blocked or challenged attempt.

Remember one sentence: protect the login itself with bcrypt hashing, rate limiting, MFA, and secure session management so a stolen password alone cannot open the vault.

---

Share
Use ← → arrow keys to navigate