Choose your stack
Fix missing authentication for a critical function in Node.js and Express
Verify a credential on the server before any critical route runs.
Open the fix card Python · FlaskFix missing authentication in Python and Flask
Authenticate the operator, do not trust the network or a client-supplied header.
Open the fix card Java · Spring BootFix missing authentication for a critical function in Java and Spring Boot
Verify a credential on the server before any critical route runs.
Open the fix card C# · ASP.NET CoreFix missing authentication for a critical function in C# and ASP.NET Core
Verify a credential on the server in your authentication handler, then require it on every protected endpoint.
Open the fix card TypeScript · Next.jsFix missing authentication for a critical function in Next.js
Verify a credential on the server in every protected route handler.
Open the fix card Python · FastAPIFix missing authentication in Python and FastAPI
Put an authentication dependency on every protected path operation.
Open the fix card Python · DjangoFix missing authentication in Python and Django
Authenticate the operator, do not trust the network or a client header.
Open the fix card Go · net/httpFix missing authentication in Go and net/http
Make every critical route go through one authentication step that verifies a credential the server issued: here a Bearer token whose HMAC-SHA256 is checked with hmac.Equal.
Open the fix card Ruby · RailsFix missing authentication in Ruby on Rails
Keep the before_action :authenticate in ApplicationController so every action needs it, and skip_before_action only on the public health route.
Open the fix card PHP · LaravelFix missing authentication in PHP and Laravel
Put every critical route behind one middleware that verifies a server-issued credential: here a Bearer token whose HMAC-SHA256 is checked with hash_equals.
Open the fix cardHow it happens
- The route has no authentication at all. A dashboard, metrics, export or internal admin endpoint is shipped reachable by anyone who knows the path (CWE-306). "It's not linked anywhere" is not authentication.
- Identity is read from the request. The handler trusts a userId in the query string or body, so the caller answers the authentication question themselves and reads any account by changing the value.
- A client-settable header is trusted. The app believes an upstream proxy sets and strips X-Authenticated-User (or X-Forwarded-User), but the service is reachable without the proxy, so a client sends the header and becomes anyone. The check exists but verifies nothing (CWE-287).
- The guard protects one route and misses a sibling. A detail endpoint is fixed after a review while a new /export or /me.json ships without the check, or a middleware matcher skips a route group.
- A present-but-unverified token is treated as proof. The code checks that a token exists rather than validating it against a session store or a signature, so any non-empty value is accepted.
Fix principles
- Treat every route as protected unless you decided it is public. Decide which one or two endpoints are genuinely public (a liveness check) and guard the rest.
- Write one authentication guard: read the credential, verify it against server-side state (a session store, or a signed token you validate), set the user from the result, and answer 401 on anything absent, unknown or malformed.
- Apply the guard on every protected route, by mounting it on the router or area rather than repeating per-handler decisions; a new route is then covered by default. Where a framework uses edge middleware, keep a check in the handler too, because matchers miss routes.
- Take the user, tenant and role only from the verified credential. Never read identity from the query string, the body, or a header the client can set.
- Keep internal-only endpoints (metrics, health detail, configuration) behind the guard, or strip them to data that is safe to expose.
- Remember authentication is not authorization: once the caller is known, still check that they may perform the specific action or read the specific object.
The scenario
An account service exposes GET /health (public), GET /me (the caller's own profile) and GET /admin/metrics (an internal figure). /me and /admin/metrics are critical functions that must not run without a caller. The credential is an opaque bearer token looked up in a server-side sessions table; the fixtures seed a few users and sessions, and the fixed guard returns the stored user or 401. The vulnerable build takes identity from the request; the wrong build trusts a client-set header.
How the cards prove it
- Run each card's tests in three modes: the fixed implementation must pass, and the vulnerable and the plausible-but-wrong implementations must fail. run-all.mjs does this for every card.
- The exploit tests call each protected route with no credential, with a forged identity header, and with a made-up token, and assert 401 with no user data. The functional tests keep a valid session and a public health route working.
- On a running system, enumerate every route that returns user or internal data and call each with no Authorization header, a forged proxy header, and a random token; each must get 401. Confirm a route added since the last review is covered by the guard.
- Do not count on static analysis: no Semgrep registry rule or CodeQL query for these stacks knows which routes must be authenticated, and the OWASP Top 10 pack reported nothing on the vulnerable code. Each card's scannerRules records what was checked.
Identifiers
CWE-306 (Missing Authentication for Critical Function) is the weakness the cards are named after: a route that returns user or internal data runs with no verified caller. CWE-287 (Improper Authentication), its parent, covers the wrong implementation, whose check exists but verifies nothing, trusting a header the client sets. Both map to OWASP Top 10:2025 A07 Authentication Failures (A07's official CWE list includes CWE-306 and CWE-287) and to API2:2023 Broken Authentication. CAPEC-12 (Choosing Message Identifier) and CAPEC-36 (Using Unpublished Interfaces or Functionality) are two of the attack patterns MITRE links to CWE-306. The 2025 CWE Top 25 does not rank CWE-306.
Attack patterns:
References
Practice in the browser
- JWT validation lab: Validate the purpose and audience of a token · Practitioner, JavaScript
Guides on this weakness
Verify webhook deliveries before acting
Verify the provider’s signature over the correct bytes, then apply replay, authorization, and idempotency controls before a business action.
Maintain an API inventory and prevent gateway bypass
Connect documented routes, deployed listeners and backend trust so retired or direct paths do not bypass the intended controls.
Defend JWTs against forgery, confusion and replay
Verify a JWT as an issuer-, type- and audience-bound assertion with an algorithm allowlist and trusted keys, closing the alg "none", key-confusion, header-injection, weak-secret and replay attacks.
Defend OAuth and OIDC against redirect and token attacks
Apply RFC 9700 and RFC 10017: exact redirect matching, PKCE with downgrade protection, state and iss binding, refresh-token rotation, sender-constrained tokens, and a backend-for-frontend so browser tokens cannot be stolen.
Publish security advisories with CSAF, VEX, CVE records and OSV
What a useful security advisory contains and how to publish it in the formats tools read: a validated CSAF 2.0 VEX document, the CSAF provider files, CVE records through a CNA (CVE Record Format 5.2), and OSV records for open-source packages. Covers the status of CSAF 2.1 as of October 2026.
Case study: ingress-nginx CVE-2025-1974 and the exposed admission webhook (2025)
How an admission webhook open to the whole Pod network made ingress-nginx's configuration-injection bugs a 9.8 cluster-takeover risk in 2025, and what its 2026 retirement means.
Case study: First American's EaglePro document exposure (2019)
How sequential IDs in unauthenticated links exposed First American's title documents in 2019, after its own penetration test found the flaw and a clerical error let it go unfixed.