Skip to main content

Broken Authentication

Broken Authentication encompasses flaws in authentication mechanisms that allow attackers to compromise passwords, session tokens, or exploit implementation flaws to assume other users' identities.

Critical CWE-287 A07:2021 Identification and Authentication Failures
Affects: C#JavaJavaScriptPHPPythonRubyGo

What is Broken Authentication?

Broken Authentication (CWE-287) refers to weaknesses in authentication mechanisms that allow attackers to compromise user accounts. Authentication is the process of verifying that a user is who they claim to be. When this process is flawed, attackers can:

  • Bypass authentication entirely — Access protected resources without any credentials
  • Impersonate any user — Exploit logic flaws to assume another user’s identity
  • Brute-force credentials — Exploit missing rate limiting to guess passwords
  • Hijack sessions — Steal or predict session tokens to take over authenticated sessions
  • Exploit credential reuse — Use credentials leaked from other breaches (credential stuffing)

Broken authentication is classified as Critical because it directly undermines the most fundamental security control. If authentication is compromised, all other access controls built on top of it become meaningless.

Why it matters

Authentication failures are consistently among the most exploited vulnerability classes:

  1. Direct account takeover — Broken authentication gives attackers full access to victim accounts, including administrative accounts
  2. Massive scale — Credential stuffing attacks test millions of stolen credentials against login endpoints automatically
  3. Cascade effect — A single authentication bypass can expose all data and functionality protected behind the login
  4. Business logic abuse — Authenticated sessions allow attackers to perform actions as the victim: transfer funds, change settings, access sensitive data
  5. Regulatory impact — Authentication failures violate virtually every compliance framework (PCI-DSS, HIPAA, SOC 2, GDPR) and can result in significant fines

The 2012 LinkedIn breach (117 million SHA-1 hashed passwords), the 2016 Uber breach (57 million user records accessed via hardcoded credentials), and countless credential stuffing campaigns demonstrate the real-world impact.

How exploitation works

Missing authentication on sensitive endpoints

# VULNERABLE: No authentication check on admin endpoint
@app.route('/admin/users/delete', methods=['POST'])
def delete_user():
    user_id = request.form.get('user_id')
    db.users.delete_one({'_id': user_id})
    return jsonify({'status': 'deleted'})

An attacker discovers this endpoint and can delete any user without logging in.

Authentication bypass via parameter manipulation

# Normal login flow
POST /login
{"username": "admin", "password": "wrong"}
→ 401 Unauthorized

# Bypass by removing the password check
POST /login
{"username": "admin"}
→ 200 OK (if backend doesn't validate presence of password)

Weak password reset

# Predictable reset token
GET /reset-password?token=123456
# Attacker enumerates tokens: 123457, 123458...

# Password reset without verifying current password
POST /api/change-password
{"user_id": "victim_id", "new_password": "attacker_password"}

Session fixation

# Attacker sets a known session ID in the victim's browser
https://target.com/login?sessionid=ATTACKER_KNOWN_SESSION
# Victim logs in → attacker's known session is now authenticated

JWT vulnerabilities

# VULNERABLE: JWT with "none" algorithm
import jwt

token = jwt.encode({"user": "admin", "role": "superadmin"}, key="", algorithm="none")
# Some libraries accept "none" algorithm, bypassing signature verification

# VULNERABLE: JWT with weak secret
token = jwt.encode({"user": "admin"}, key="secret", algorithm="HS256")
# "secret" can be brute-forced in seconds

Vulnerable code examples

C# / ASP.NET — Missing authentication

// VULNERABLE: No [Authorize] attribute on sensitive endpoint
[HttpPost("admin/settings")]
public IActionResult UpdateSettings([FromBody] AppSettings settings)
{
    _settingsService.Update(settings);
    return Ok();
}
// VULNERABLE: Comparing passwords in plain text
public bool ValidateUser(string username, string password)
{
    var user = _db.Users.FirstOrDefault(u => u.Username == username);
    return user != null && user.Password == password;  // Plain text comparison
}

Java / Spring — Weak session management

// VULNERABLE: Session ID not regenerated after login
@PostMapping("/login")
public String login(@RequestParam String username, @RequestParam String password,
                    HttpSession session) {
    if (authService.validate(username, password)) {
        session.setAttribute("user", username);  // Same session ID as before login
        return "redirect:/dashboard";
    }
    return "redirect:/login?error";
}

Python / Django — No rate limiting

# VULNERABLE: No rate limiting, enables brute force
def login_view(request):
    username = request.POST.get('username')
    password = request.POST.get('password')
    user = authenticate(request, username=username, password=password)
    if user is not None:
        login(request, user)
        return redirect('/dashboard')
    return render(request, 'login.html', {'error': 'Invalid credentials'})

Node.js — Insecure JWT implementation

// VULNERABLE: No algorithm restriction, weak secret
const jwt = require('jsonwebtoken');

app.post('/login', (req, res) => {
    const { username, password } = req.body;
    if (validateCredentials(username, password)) {
        const token = jwt.sign(
            { user: username, role: 'admin' },
            'mysecret'  // Weak, hardcoded secret
        );
        res.json({ token });
    }
});

app.get('/api/data', (req, res) => {
    const token = req.headers.authorization?.split(' ')[1];
    const decoded = jwt.verify(token, 'mysecret');  // No algorithm restriction
    // ...
});

PHP — Insecure password storage

// VULNERABLE: MD5 password hashing
function register($username, $password) {
    $hash = md5($password);  // MD5 is cryptographically broken
    $db->query("INSERT INTO users (username, password) VALUES (?, ?)",
               [$username, $hash]);
}

function login($username, $password) {
    $hash = md5($password);
    $user = $db->query("SELECT * FROM users WHERE username = ? AND password = ?",
                       [$username, $hash]);
    return $user ? true : false;
}

Secure code examples

C# — Secure authentication with Identity

// SECURE: Require authentication, use proper password hashing
[Authorize(Roles = "Admin")]
[HttpPost("admin/settings")]
public IActionResult UpdateSettings([FromBody] AppSettings settings)
{
    _settingsService.Update(settings);
    return Ok();
}

// SECURE: Use BCrypt for password verification
public bool ValidateUser(string username, string password)
{
    var user = _db.Users.FirstOrDefault(u => u.Username == username);
    if (user == null) return false;
    return BCrypt.Net.BCrypt.Verify(password, user.PasswordHash);
}

// SECURE: Register with proper hashing
public void RegisterUser(string username, string password)
{
    string hash = BCrypt.Net.BCrypt.HashPassword(password, workFactor: 12);
    _db.Users.Add(new User { Username = username, PasswordHash = hash });
    _db.SaveChanges();
}

Java — Secure session management

// SECURE: Regenerate session ID after login, set secure attributes
@PostMapping("/login")
public String login(@RequestParam String username, @RequestParam String password,
                    HttpServletRequest request) {
    if (authService.validate(username, password)) {
        // Invalidate old session, create new one (prevents session fixation)
        request.getSession().invalidate();
        HttpSession newSession = request.getSession(true);
        newSession.setAttribute("user", username);
        newSession.setMaxInactiveInterval(1800); // 30-minute timeout
        return "redirect:/dashboard";
    }
    return "redirect:/login?error";
}

Python — Rate limiting and secure authentication

# SECURE: Rate limiting + account lockout
from django.contrib.auth import authenticate, login
from django_ratelimit.decorators import ratelimit

@ratelimit(key='ip', rate='5/m', method='POST', block=True)
@ratelimit(key='post:username', rate='10/h', method='POST', block=True)
def login_view(request):
    username = request.POST.get('username')
    password = request.POST.get('password')

    # Check for account lockout
    if LoginAttempt.is_locked(username):
        return render(request, 'login.html',
                      {'error': 'Account temporarily locked. Try again later.'})

    user = authenticate(request, username=username, password=password)
    if user is not None:
        LoginAttempt.reset(username)
        login(request, user)
        request.session.cycle_key()  # Regenerate session ID
        return redirect('/dashboard')

    LoginAttempt.record_failure(username)
    return render(request, 'login.html', {'error': 'Invalid credentials'})

Node.js — Secure JWT implementation

// SECURE: Strong secret, explicit algorithm, proper expiration
const jwt = require('jsonwebtoken');
const crypto = require('crypto');

const JWT_SECRET = process.env.JWT_SECRET; // 256-bit key from environment
const JWT_OPTIONS = {
    algorithm: 'HS256',
    expiresIn: '1h',
    issuer: 'myapp'
};

app.post('/login', async (req, res) => {
    const { username, password } = req.body;
    const user = await User.findOne({ username });
    if (!user || !(await bcrypt.compare(password, user.passwordHash))) {
        // Generic error message prevents user enumeration
        return res.status(401).json({ error: 'Invalid credentials' });
    }
    const token = jwt.sign(
        { sub: user.id, role: user.role },
        JWT_SECRET,
        JWT_OPTIONS
    );
    res.json({ token });
});

app.get('/api/data', (req, res) => {
    const token = req.headers.authorization?.split(' ')[1];
    try {
        const decoded = jwt.verify(token, JWT_SECRET, {
            algorithms: ['HS256'],  // Explicitly restrict algorithms
            issuer: 'myapp'
        });
        // ...
    } catch (err) {
        res.status(401).json({ error: 'Invalid token' });
    }
});

What Offensive360 detects

Our SAST engine analyzes authentication implementations for common weaknesses. We detect:

  • Missing authentication — Sensitive endpoints (admin panels, data modification, user management) without authentication middleware or annotations ([Authorize], @PreAuthorize, login_required)
  • Weak password hashing — Use of MD5, SHA-1, SHA-256, or other fast hashes for password storage instead of bcrypt, scrypt, or Argon2
  • Hardcoded credentials — Passwords, API keys, or secrets embedded in source code (see also CWE-798)
  • Insecure JWT configuration — Missing algorithm restrictions, weak secrets, missing expiration, "none" algorithm acceptance
  • Session management flaws — Missing session regeneration after login, overly long session timeouts, session tokens in URLs
  • Missing rate limiting — Login endpoints without brute-force protection
  • User enumeration — Different error messages or response times for valid vs. invalid usernames
  • Insecure password reset — Predictable reset tokens, missing token expiration, password changes without current password verification

Each finding identifies the specific authentication weakness and its location in the code.

Remediation guidance

  1. Use established authentication frameworks — ASP.NET Identity, Spring Security, Django’s auth system, and Passport.js are battle-tested. Don’t build custom authentication from scratch.

  2. Hash passwords with bcrypt, scrypt, or Argon2 — These algorithms are deliberately slow and include automatic salting. Never use MD5, SHA-1, or even plain SHA-256 for password storage.

  3. Implement multi-factor authentication (MFA) — Require a second factor (TOTP, WebAuthn, SMS) for sensitive accounts and operations.

  4. Rate-limit authentication endpoints — Limit login attempts per IP and per account. Implement progressive delays or temporary lockouts after repeated failures.

  5. Regenerate session IDs — After successful authentication, invalidate the old session and issue a new session ID to prevent session fixation.

  6. Use secure JWT practices — Use strong secrets (256+ bits), explicitly specify allowed algorithms, set reasonable expiration times, and validate all claims.

  7. Prevent user enumeration — Return identical error messages and response times for valid and invalid usernames. Use generic messages like “Invalid credentials.”

  8. Secure password reset flows — Use cryptographically random tokens, enforce short expiration (15-30 minutes), invalidate tokens after use, and require current password for password changes.

False-positive considerations

Offensive360 may flag authentication patterns that are intentionally open:

  • Public endpoints — Endpoints that are intentionally unauthenticated (public APIs, health checks, login pages themselves)
  • Authentication middleware applied globally — If authentication is enforced at the middleware/filter level, individual endpoints may not have explicit annotations
  • Custom authentication frameworks — Proprietary authentication mechanisms that the analyzer doesn’t recognize as authentication checks
  • Test environments — Authentication disabled in test configurations

References

Author: Offensive360 Security Research
Last reviewed: March 1, 2026

Detect Broken Authentication in your code

Run Offensive360 SAST against your codebase to find this and hundreds of other vulnerabilities.