Introduction
The OAuth 2.0 authorization framework is designed to improve security by delegating limited access to third-parties without sharing credentials. In our previous blog post on OAuth 2.0 we discussed how OAuth 2.0 implementations should be secured. Unfortunately, it is common for vulnerabilities to be introduced with OAuth 2.0 implementations, particularly on the side of authorization servers. This is often due to neglecting or misinterpreting parts of the specification. The purpose of this blog post is to explore some of the most common vulnerabilities to occur with OAuth 2.0 authorization server implementations. Additionally, we will showcase a tool called KOAuth, which was created to automate testing for these common vulnerabilities in a browser environment.
Improper Redirect URI Validation
The first authorization server vulnerability class we will discuss is improper redirect URI validation. Generally, when OAuth 2.0 client applications are created, one or more redirect URIs are stored with the authorization server as trusted callback locations. Later, during authorization requests, the client must provide a valid redirect URI in a query parameter, indicating where the user should be redirected to with their authorization code or access token (depending on the OAuth 2.0 flow in use). It is crucial that the authorization server properly validates the provided redirect URI to be equal to one of the allow-listed redirect URIs for the client. If this validation is insufficient, it can lead to a compromised access token. Consider the following example, where a user of a client application is going through the OAuth 2.0 implicit flow. For this example, assume that the trusted redirect URL for the client application has been set to “https://example.com/callback”.
[image id=”2771″ caption=”Note: URL encoding of query parameters excluded for readability” filter=”false”]
Shown above, the authorization server does not properly validate the domain name in the provided URL. Instead, it only validates that “example.com” is present in the domain. If the validation of the “redirect_uri” parameter is insufficient, an access token can be sent to an attacker’s server.
Sometimes the authorization server will only validate the domain of the provided “redirect_uri” value, ignoring the URL path. Some popular OAuth 2.0 providers allow this as a configurable setting, such as Facebook. This opens the door to the possibility of a similar, but different, attack. Imagine a client application has an Open Redirect vulnerability present at the “/redirect?url=<ARBITRARY_URL_HERE>” endpoint. Normally, this would be a relatively low-risk issue; however, in this case, an attacker can provide a “redirect_uri” value that points to the open redirection endpoint on the trusted domain of the client application. After being redirected to the vulnerable endpoint, the user will be redirected again based on the “url” value provided. This process is shown below:
[image id=”2770″ caption=”Note: URL encoding of query parameters excluded for readability” filter=”false”]
It is quite often the case that authorization servers do not strictly validate the “redirect_uri” value for equivalence. Assuming the trusted “redirect_uri” value is “https://example.com/callback”, the following shows several examples of URLs to use to attempt to identify improper validation by the authorization server:
- Add higher level or lower level domains
a. https://example.com.attacker.com/callback
b. https://attacker.example.com/callback - Place localhost in the domain name, as sometimes authorization servers will allow this for debugging purposes
a. https://localhost.attacker.com/callback
b. https://localhost/callback
c. http://localhost/callback - Downgrade the scheme of the trusted redirect URL, allowing for tokens to be leaked over cleartext
a. http://example.com/callback - Alter or append to the URL path, identifying if strict path validation is present
a. https://example.com/changed
b. https://example.com/callbackAppendedTo - Attempt to identify URL parsing issues
a. https://example.com@attacker.com/callback
b. https://example.com#attacker.com/callback
Consent Screen Attacks
Scope Confusion
Although it is not explicitly outlined in the original OAuth 2.0 specification, authorization servers will generally prompt a user for consent during the OAuth 2.0 flow, especially if it is the first time the user is authorizing a specific client application. You are probably familiar with a screen similar to the following:
The consent screen is generated by the authorization server and shown to the user. Its intent is to display clearly to the user what access they are granting to the client application, and to ensure they consent to the requested scopes. If consent screens are not properly generated and secured, they may be vulnerable to multiple issues.
The first vulnerability we will explore is consent screens improperly displaying which scopes are actually being requested. In the OAuth 2.0 flow, a user sends a request to the authorization server with a “scope” query parameter, defining which scopes are being requested by the client application. Additionally, when creating client applications, authorization servers often allow access scopes to be specified for the application. If the scopes listed in the “scope” parameter during the OAuth 2.0 authorization request differ from these predefined scopes, this often leads to inconsistencies in the OAuth 2.0 consent screen and the actual scopes assigned to the resulting access token
For example, when registering a client application with the authorization server, the user creating the application may be asked to specify which scopes the application requests (depending on the authorization server’s implementation). Assume the client application is registered with the authorization server with the following 3 scopes defined: “firstName”, “lastName”, and “email”. Now, if a user provides a “scope” parameter with a value of “firstName” during the OAuth 2.0 authorization request, it is unclear of whether the resulting access token will be granted only the “firstName” scope, or if it will be granted all 3 scopes defined when creating the application. The OAuth 2.0 specification does not seem to specify what exactly should happen in this scenario. As long as the consent screen that is generated during the flow accurately represents what the user will be granting permission to, this should not be an issue. However, Praetorian engineers have identified the following two vulnerable scenarios while performing security assessments of various authorization servers:
- The authorization server reflects the scopes specified by the “scope” query parameter in the consent screen, but the resulting access token retrieved upon completion of the flow contains the scopes that were specified when creating the application with the authorization server.
- The authorization server displays only the scopes that were configured when creating the application in the consent screen, but the resulting access token retrieved upon completion of the flow grants access to the scopes that were specified in the “scope” query parameter in the authorization request.
Both of these scenarios may lead to a user granting more consent to an application than they would have allowed if they were properly informed. This may allow an attacker to entice a user to install their malicious application, because a user may believe they are only granting access to their first name, or something similarly benign.
Clickjacking
Another potential vulnerability involving the consent screen is Clickjacking. Put simply, Clickjacking attacks are social engineering attacks which attempt to trick users into clicking buttons that perform sensitive actions. Typically, this works by embedding the vulnerable page within an attacker’s page using an HTML iframe element. The embedded content would generally be configured to appear invisible, located on top of content on the malicious page. Then, the attacker’s malicious web page persuades the victim to click a button on the page, which actually clicks a button on the invisible embedded content. For example, a user may believe they are clicking a “Sign In” button on the malicious page, but they actually click the “Authorize” button on a vulnerable OAuth 2.0 authorization server, authorizing a malicious application. This attack is generally possible if each of the following conditions are met:
- The victim user is currently authenticated to the authorization server in their browser context.
- The authorization server uses Cookies for authentication.
- The authorization server does not explicitly disallow iframes at the consent endpoint.
In most cases, allowing iframes to your application does not create immediate and serious security risks. However, in the case of OAuth 2.0 authorization servers, which generally perform very sensitive actions (granting access to one’s account) with a single click, it is essential that iframes are disallowed from untrusted sources.
PKCE Downgrade Attack
Proof Key for Code Exchange (PKCE) is an extension of the OAuth 2.0 framework that adds additional security and allows public clients to perform the authorization code flow. This extension is described in detail in part one of this blog series. One aspect of PKCE that was not discussed was the “plain” code challenge method.
When the code challenge is being sent by the client to the authorization server during the authorization request, the client must include a “code_challenge_method” parameter, which should generally be set to “S256”. This indicates that the provided code challenge is the result of computing the SHA-256 hash of the code challenge. The PKCE specification also requires that authorization servers support a “code_challenge_method” value of “plain”, which indicates that the code challenge is equal to the code verifier. The purpose of this method is to allow support for clients which are not capable of supporting “S256” for technical reasons. The PKCE flow will proceed as normal when “plain” is in use, with the only difference being the code verifier provided during the token exchange request is equal to the code challenge.
One possible vulnerability that could arise from supporting the “plain” code challenge method is a downgrade attack. One of the security benefits clients receive by using PKCE for their OAuth 2.0 flows is that an attacker sniffing network traffic cannot intercept and use an authorization code. Consider the following scenario:
- A user begins the authorization code flow, sending the following in their authorization request:
“code_challenge_method=S256&code_challenge=HaE7…s3h4ef” - An attacker on the user’s device or network sniffs the “code_challenge” value
- The authorization server responds with an authorization code
- An attacker intercepts the authorization code in the HTTP 302 response
At this point, generally the user would be protected by PKCE because the attacker cannot guess the code verifier value. However, an attacker may attempt to downgrade from “S256” to “plain” by sending the code challenge value as the code verifier in the exchange request. The “code_challenge_method” parameter is only included in the authorization request, not the token exchange request. This means that the authorization server must record whether the client began the flow using “S256” or “plain”, and use this to inform the expected code verifier value. If the code challenge method is not recorded and enforced, the authorization server might handle the exchange request in the following manner:
- Compute the SHA-256 hash of the provided code verifier, and compare it to the stored code challenge
- If the resulting hash is not equal to the stored code challenge, assume the “plain” method was in use
- If the code challenge is equal to the code verifier, the request succeeds
In this scenario, the previously described attacker can defeat the protections offered by PKCE. To prevent downgrade attacks, the authorization server must associate both code challenges and code challenge methods with ongoing authorization code flows, enforcing the same code challenge method sent for the authorization request is used to validate the token exchange request.
Cross-Site Request Forgery
This section will be brief, since the importance of clients using and requiring the “state” parameter, which is recommended by the OAuth 2.0 specification, was covered in part one of this blog series. From the perspective of the authorization server, the “state” parameter only needs to be supported. That is to say, if a “state” query parameter was provided during a user’s authorization request, a “state” parameter with an equal value should be included in the resulting redirect to the client application.
Introducing KOAuth
KOAuth is an automated dynamic scanner designed to identify various vulnerabilities in OAuth 2.0 authorization servers, ranging from high-risk implementation flaws to missing best practices. Traditionally, it has been difficult to create a reusable dynamic scanner for OAuth 2.0 for a variety of reasons, including:
- OAuth 2.0 HTTP redirects can be implemented in various ways, ranging from simple HTTP 302 redirects to JavaScript redirects (often influenced by values stored in local storage, IndexedDB, etc.)
- Web applications often require an active session, updating session Cookies to new values on each request
- Implementations often diverge from the OAuth 2.0 specification in small but frustrating ways (e.g. requiring consent to be obtained each time a user initiates the OAuth 2.0 flow)
KOAuth is a tool designed to address some of these common challenges in automated OAuth 2.0 implementation testing. KOAuth is written in Go and uses the “chromedp” package to automate testing in a Chrome browser context. The tool provides configurable CLI options to support testing different authorization server implementations, such as authorization servers that require users to authenticate at a separate URL and authorization servers that expect JSON bodies for token exchange requests. While the tool provides 22 validation checks by default, custom checks can be added readily using a simple, pre-defined JSON check schema. Upon completion of the scan, an HTML report is generated with the results.
[image id=”2773″ filter=”false”]
KOAuth can be found on GitHub here.