CWE-287, Improper Authentication, applies when an actor claims to have a given identity and the product does not prove, or insufficiently proves, that the claim is correct.
MITRE classes it as a broad weakness with many narrower children, among them authentication bypass by alternate path (CWE-288), by alternate name (CWE-289), and by spoofing (CWE-290). It spans web, ICS and OT, and technology independent systems.
Anyone who builds or integrates a login, an API key check, a single sign-on flow, a proxy, or a device pairing step can introduce it.
How it happens
The mechanism is "who are you" going unanswered or being answered weakly. MITRE's examples show how many ways that occurs:
- CVE-2024-11680 (Gold): a file sharing PHP product does not check whether the user is logged in during requests for PHP library files under an includes directory, allowing configuration changes, code execution, and other impacts.
- CVE-2022-35248 (Gold): a chat application skips validation when Central Authentication Service is enabled, effectively removing the second factor from two factor authentication.
- CVE-2022-36436 (Gold): a Python based authentication proxy does not enforce password authentication during the initial handshake, allowing the client to bypass authentication by specifying a "None" authentication type.
- CVE-2022-30034 (Gold): MITRE describes a chain in which the web UI for a Python RPC framework does not use regex anchors to validate user login emails, potentially allowing a bypass of OAuth.
Common causes behind these:
- Unprotected files and routes. Authentication is applied to pages but not to support files.
- A bypass branch. A feature flag or mode skips the check entirely.
- Letting the client choose. The client states the method, including "none".
- Sloppy matching. A pattern without anchors accepts more than intended.
Code showing the client controlled method flaw:
# Vulnerable: the client picks the authentication method
def authenticate(request):
method = request.headers.get("X-Auth-Method", "password")
if method == "none":
return User.anonymous_but_trusted()
if method == "password":
return check_password(request)
And the unanchored match:
import re
# Vulnerable: matches anywhere in the string
if re.search(r"@example\.com", email):
allow_login(email)
# "attacker@example.com.evil.net" and "x@example.com@evil.net" pass
What an attacker gains
MITRE lists a broad set: integrity, confidentiality, and availability impact, along with access control effects. Specifically, reading application data, gaining privileges or assuming another identity, and executing unauthorized code or commands. This weakness can expose resources or functionality to unintended actors, which may lead to the full range of compromise. The CVE examples above include configuration changes and code execution.
How to find it
MITRE notes that automated static analysis can detect certain authentication patterns and may analyze related configuration files such as .htaccess. Manual analysis, such as penetration testing and threat modeling, is also useful, as are web application scanners in dynamic analysis. Combine them:
- List every entry point. Pages, API routes, static includes, file downloads, websockets, admin consoles, device management ports.
- Test with no credentials. Request each one without a session.
- Test each factor. For multi factor authentication, check every path that completes a login, including single sign-on, recovery, and API token flows.
- Tamper with negotiation. Change method fields, headers, and handshake options to the weakest value.
- Read the matching code. Look for unanchored patterns, string comparisons on user input, and any branch that returns a logged in user without a credential check.
Try it yourself
The demo below runs a web scan that finds pages reachable without logging in. Watch how it reports unauthenticated routes, then run the no credentials test on your own application.
Click through it at your own pace, or open it full screen.
How to fix it
- Authenticate at one choke point. Apply authentication in middleware or a gateway so every route is covered unless it is explicitly marked public.
- Fail closed. A missing, malformed, or unknown credential means rejection.
- Never let the client choose the weakest option. Fix the allowed methods on the server and reject "none".
- Do not build your own. Use a well maintained library or identity provider for passwords, tokens, and multi factor flows.
- Anchor your matches. Use full string comparison or anchored patterns when validating identities, and prefer exact comparison on a normalized value.
- Cover every path to a session. Recovery, single sign-on, and API tokens must be at least as strong as the main login.
- Keep authentication components current. The proxy, framework, and plugin examples above were product flaws. Track the libraries you depend on.
Corrected versions:
ALLOWED = {"password", "token"}
def authenticate(request):
method = request.headers.get("X-Auth-Method", "password")
if method not in ALLOWED:
raise Unauthorized()
return AUTH_HANDLERS[method](request)
import re
if re.fullmatch(r"[A-Za-z0-9._%+-]+@example\.com", email):
allow_login(email)
To see whether a dependency in your stack carries a known authentication flaw, run the free in-browser scan at gold.safeguard.sh/scan. It needs no login, keeps the file on your machine, and handles up to 100 dependencies.
Quick check
What went wrong in the "None" authentication type example?
The proxy accepted a client supplied method that meant no authentication, so a client could skip the password step. The server should decide the allowed methods and reject anything else.
Why is an unanchored regex a security problem?
It matches a pattern anywhere in the input, so an attacker can craft a string that contains the expected text but belongs to a different identity.
Is multi factor authentication enough on its own?
Only if every path to a session enforces it. In CVE-2022-35248, enabling one login mode removed the second factor.
Sources
- MITRE CWE-287: https://cwe.mitre.org/data/definitions/287.html
- Safeguard Gold entry: https://gold.safeguard.sh/cwe/CWE-287