Skip to content

Authentication Bypass

Overview

An API endpoint that required authentication returned a successful response for a request with invalid credentials. Protected endpoints are expected to return a 4xx status code indicating a client error. A server will usually return "403 Forbidden" or an "HTTP 401 Unauthorized" when credentials are missing or invalid.

Such a successful response indicates that an unauthorized user may access a sensitive endpoint, potentially leaking information or performing unauthorized operations. Authentication bypasses are security vulnerabilities.

Recommendation

Review the endpoint code to ensure that the required authentication code is running in all cases. If the endpoint does not require authentication, update the specification to indicate as such.

If the authentication checks are performed on this endpoint, review the code checking the provided credentials are valid. Ensure that the user-provided data is validated extensively before any authentication checks are performed. The sample response provided by Mayhem for API as part of the bug report may point you in the right direction when trying to debug the issue. The issue may be in the underlying library, if any, you are using to perform validation checks.

Examples

Authentication bypass might be as simple as a missing annotation. In the context of Java Spring Boot for instance, you might need to go from:

@RequestMapping("/super-secret")
public String protectedEndpoint() {
  ...
}

to the following endpoint that requires an authenticated user:

@RequestMapping("/super-secret")
public String protectedEndpoint(@AuthenticationPrincipal User user) {
  ... // do stuff with user
}

The specific changes you have to make will depend on how you have setup authorization in your application. Check out the Spring Security Architecture for more information.

JSON Web Tokens (JWTs) are commonly used to generate API credentials. JWT tokens are a self-contained way for securely transmitting information between the client and server as a JSON object. The tokens are signed, allowing the receiver to verify the integrity of the JSON. Tokens may also be encrypted in order to provide secrecy.

In its compact form, JWT consist of three parts separated by dots (.), which are: - Header - Payload - Signature

Therefore, a JWT typically looks like the following: xxxxx.yyyyy.zzzzz. Importantly, the header contains the signing algorithm used to verify the payload. The algorithm is used by the receiver to verify the integrity of the message. As you can see, this is a bit of a chicken or the egg problem: the receiver is relying on unverified information from the JWTs to verify its integrity.

Most of the JWT library used to have an API that looked like:

verify(user_provided_jwt, verification_key)

An attacker can abuse this. A few years ago, many JWT libraries validated JWTs that had the algorithm changed to 'none', entirely disabling the integrity checks! There were also more elaborate attacks relying on switching the algorithm from RSA to HMAC.

Nowadays, most JWT library changed their API to allow the verifier to ignore the algorithm in the JWT header in favor of a hard-coded one:

verify(user_provided_jwt, algorithm, verification_key)

References

  • Common Weakness Enumeration: CWE-287.
  • Common Weakness Enumeration: CWE-290.
  • Common Weakness Enumeration: CWE-807.