Skip to content

CWE-287: Improper Authentication

Overview

Improper authentication occurs when an application fails to correctly verify the identity of users, services, or systems. Weak passwords and credential stuffing exploit the credentials themselves; improper authentication is a flaw in the authentication logic, the mechanism that checks "you are who you claim to be". An attacker who finds such a flaw passes as a legitimate user or service without ever presenting valid credentials, and reaches whatever that identity is permitted to do.

Relationship to Other CWEs

CWE-287 is a MITRE Class, one level below the CWE-284 (Improper Access Control) Pillar, and MITRE marks it Discouraged for direct mapping since a more specific descendant usually applies better.

Its direct children in MITRE's research view:

A second group appears under CWE-287 only in MITRE's simplified mapping view (view-1003), which MITRE publishes for categorising public vulnerability data such as NVD's - so a finding routed through a CVE record may arrive here carrying one of these. In the research view they sit elsewhere:

CWE-307, CWE-521 and CWE-640 have no dedicated page here yet. If a finding names one of these specifically, prefer its own remediation; use this page when the finding is reported generically as an authentication weakness.

OWASP Classification

A07:2025 - Authentication Failures

Risk

High to Critical: The impact depends on which identity an attacker can assume and what it is permitted to do:

  • Unauthorized access: Attackers access accounts, data, or functions without valid credentials
  • Account takeover: Bypass authentication to fully control user accounts
  • Unauthorized identity establishment: Impersonate another user or service before authorization checks run
  • Data breaches: Access sensitive information belonging to other users
  • Compliance violations: Can fail applicable SOX, PCI DSS, HIPAA or GDPR authentication requirements, depending on the organisation, system and data involved
  • Business logic bypass: Access payment, ordering, or workflow functions without authenticating
  • API abuse: Exploit unauthenticated or weakly authenticated API endpoints

In regulated industries or applications handling sensitive data, authentication failures can trigger mandatory breach disclosure and significant penalties.

Remediation Steps

Core Principle: Never trust client-supplied identity; every request must be authenticated by the server using a complete, validated authentication mechanism before any identity or session is accepted.

Trace authentication flows and identify weaknesses

  • Identify every authentication entry point: login forms, API authentication, SSO, OAuth, password reset
  • Examine how credentials are validated and look for bypass conditions
  • Review how session tokens are generated, stored, timed out, and invalidated
  • Identify which endpoints and functions require authentication
  • Find resources with no authentication check, especially admin functions and APIs

Implement complete server-side authentication checks (Primary Defense)

  • Validate credentials or tokens on the server: Verify passwords, API keys, session IDs, JWT signatures, issuer/audience, expiration, and revocation status as appropriate for the mechanism.
  • Bind identity to a server-validated principal: Do not trust client-supplied user IDs, roles, or login flags.
  • Fail closed: Missing, malformed, expired, or invalid credentials must result in 401 Unauthorized.
  • Regenerate sessions after login: Prevent session fixation by issuing a fresh session identifier after successful authentication.
  • Protect all authenticated entry points: Apply authentication middleware/guards consistently to APIs, admin interfaces, and file endpoints.

Add multi-factor authentication for higher assurance

  • Use multiple independent factors to verify identity:
    1. Something you know (password, PIN)
    2. Something you have (hardware token, phone, authenticator app)
    3. Something you are (biometric: fingerprint, face recognition)
  • Require MFA: Mandatory for administrative, privileged, and other sensitive accounts, and offered to all other users
  • Use standards: TOTP (RFC 6238), WebAuthn, FIDO2
  • Support app-based authenticators: Google Authenticator, Authy, Microsoft Authenticator
  • Provide backup codes: Users can regain access if they lose their device
  • Treat SMS/voice as restricted or lower assurance: Prefer phishing-resistant authenticators such as WebAuthn/FIDO2 where possible; SMS is vulnerable to SIM swapping and number reassignment.
  • Why this works: MFA reduces the chance that password compromise alone leads to account compromise. It strengthens authentication but does not replace server-side credential/session validation and can still be bypassed by phishing or weak recovery flows.

Use proven authentication frameworks (Defense in Depth)

  • Don't build authentication from scratch: Use an established, maintained framework
  • Modern frameworks provide:
    • Secure password hashing (bcrypt, Argon2)
    • Session management with secure defaults
    • CSRF protection
    • Rate limiting
    • Account lockout mechanisms
  • Recommended options:
    • Java: Spring Security, Apache Shiro, Keycloak
    • .NET: ASP.NET Core Identity, OpenIddict, Duende IdentityServer
    • Python: Django auth, Flask-Security, Authlib
    • Node.js: Passport.js, Auth0, Auth.js/NextAuth
    • Standards: OAuth 2.0, OpenID Connect providers

Enforce authentication on all protected resources

  • Default to "deny all": Require explicit authentication grants
  • Check at application layer: Don't rely solely on web server authentication
  • Verify every request: Don't assume session validity
  • Protect all endpoints: API endpoints, admin interfaces, file access
  • Hand off to authorization: After authentication, use authorization controls such as RBAC/ABAC to decide what the authenticated principal may access.
  • Review indirect access: Direct object references, file paths

Implement account security controls

  • Rate limiting:
    • Limit failed login attempts (e.g., 5 attempts per 15 minutes)
    • Implement progressive delays after failed attempts
    • Use CAPTCHA or other risk-based challenges after multiple failures, weighing the accessibility and usability cost
    • Monitor for distributed brute force attacks
  • Account lockout:
    • Temporary lockout or progressive throttling after repeated failures
    • Avoid permanent or easily-triggered lockouts that let attackers deny service to users
    • Require account recovery process or admin unlock only for high-risk cases
    • Alert users to suspicious login attempts
  • Monitoring:
    • Log all authentication events (success and failure)
    • Alert on unusual patterns (impossible travel, new devices)
    • Track failed attempts by IP and username

Test authentication controls

  • Test authentication bypass attempts: Missing credentials, empty passwords, SQL injection in login
  • Test session management: Session fixation, session hijacking, concurrent sessions
  • Test account lockout: Verify lockout after failed attempts
  • Test MFA: Attempt bypass, test backup codes, verify enforcement
  • Test password reset: Check that reset tokens are generated and validated securely
  • Verify all protected resources require authentication
  • Use automated scanners and penetration testing
  • Re-scan with the security scanner to confirm the issue is resolved

Access Control Testing

Test scenarios:

  • Access protected browser page without authentication → Should redirect to login; API requests should return 401 Unauthorized
  • Access protected resource with invalid token → Should return 401 Unauthorized
  • Access admin function as regular user → Should return 403 Forbidden from authorization controls
  • Logout and attempt to reuse session token → Should require re-authentication

Brute Force Protection Testing

# Attempt multiple failed logins

for i in {1..10}; do
  curl -X POST https://app.com/login \

    -d "username=admin&password=wrong$i"

done

Expected result: After threshold (e.g., 5 attempts), receive rate limit error or account lockout.

MFA Verification Testing

  • Attempt login with valid password but no MFA code → Should require MFA
  • Attempt login with valid password and invalid MFA code → Should reject
  • Attempt to reuse MFA code → Should reject (prevent replay attacks)
  • Verify backup codes work when primary MFA unavailable

Session Security Testing

  • Verify session tokens are cryptographically random
  • Check session cookies have Secure, HttpOnly, SameSite attributes
  • Test session timeout (idle sessions should expire)
  • Verify concurrent session limits work (if implemented)

Authentication Bypass Testing

Common bypass attempts:

  • Remove authentication headers
  • Use different HTTP methods (POST vs GET)
  • Add parameters like debug=true, admin=1
  • Access resources via direct URL/path manipulation
  • Test API endpoints separately from UI

Common Vulnerable Patterns

Authentication weaknesses commonly take these forms:

  • Missing authentication on critical functions: Admin panels, APIs, or sensitive operations accessible without login
  • Weak or predictable authentication tokens: Session IDs, JWT secrets, or API keys easily guessed or brute-forced
  • Authentication bypass through trusted client state: Accepting user IDs, roles, or login flags from request parameters, cookies, or headers without server-side validation
  • Insecure "remember me" functionality: Persistent tokens that never expire or can be stolen
  • Session fixation vulnerabilities: Attacker sets known session ID before user authenticates
  • Inadequate protection against automated attacks: No rate limiting, CAPTCHA, or account lockout mechanisms
  • A login that returns before verifying the password when the account does not exist: The failure message is identical either way, but the two branches cost different amounts - a real account pays for a password hash, a missing one pays for nothing - so response time answers "does this username exist". Measured across this CWE's language guidance the gap ran from 4,000x to a million to one. The fix is to verify the submitted password against a fixed, genuine hash and discard the result, so both branches do the same work; see CWE-385 for the general shape and the language pages below for the concrete call. It generalises past login to any lookup-then-verify flow: password reset token redemption, API key checks, and TOTP validation.

Common Pitfalls

  • Trusting a client-supplied identity claim: Accepting a "logged in" cookie flag, an unsigned token, or a JWT whose signature/issuer/audience/expiration is never actually verified server-side - an attacker who can set or forge that value is then treated as authenticated without ever proving it.
  • Treating MFA as optional or client-enforced: Prompting for a second factor in the UI but still issuing a valid session if the client simply skips that step or calls the login API directly - MFA must be enforced as a required part of the server-side authentication flow, not a UI-only gate.
  • Rolling a custom authentication scheme instead of using a maintained framework: Hand-written session/token generation, password comparison, or password-reset flows tend to miss subtle issues (timing attacks, weak randomness, missing rate limiting) that established libraries usually handle correctly.
  • Assuming the framework closes every gap, without checking which ones: "use the framework" is the right default but not a complete answer. On the username-enumeration timing channel above, Django's ModelBackend and Spring Security's DaoAuthenticationProvider do hash on the unknown-user branch, while ASP.NET Core Identity's SignInManager.PasswordSignInAsync(string userName, ...) and Laravel's Auth::attempt() return without hashing at all - measured at 22,000x on the first of those. Check the specific control on the specific framework rather than treating the whole class of issue as handled.
  • Authenticating at the perimeter only: Enforcing login at a gateway, load balancer, or UI layer while backend APIs and internal service-to-service calls trust the request implicitly - any endpoint reachable directly (mobile app, script, another internal service) bypasses perimeter-only checks entirely.

Language-Specific Guidance

  • C# - ASP.NET Core Identity, cookie/JWT authentication handlers, TokenValidationParameters
  • Go - net/http middleware, golang-jwt signing-method validation, bcrypt password checks
  • Java - Spring Security AuthenticationProvider, JWT validation with jjwt/NimbusJwtDecoder, session fixation
  • JavaScript/Node.js - Passport.js strategies, jsonwebtoken algorithm allowlisting, session regeneration
  • PHP - password_verify vs loose comparison, firebase/php-jwt Key binding, session_regenerate_id
  • Python - Django authentication backends, Flask-Login, PyJWT algorithm allowlisting

Additional Resources