CORS Misconfiguration
Last updated
Last updated
Cross-origin resource sharing (CORS) is a browser mechanism which enables controlled access to resources located outside of a given domain. It extends and adds flexibility to the same-origin policy (SOP).
The CORS standard works by adding new HTTP headers that let servers describe which origins are permitted to read that information from a web browser. A web application executes a cross-origin HTTP request when it requests a resource that has a different origin from its own.
For HTTP request methods that can cause side-effects on server data, the specification mandates that browsers preflight the request, soliciting supported methods from a server with the HTTP OPTIONS
request method, and then, upon approval from a server, sending the actual request.
Servers can also inform clients whether credentials should be sent with requests.
CORS failures result in errors, but specifics about the error are not available to JavaScript.
Simple requests do not trigger a CORS preflight. A simple request is one that meets all the following conditions:
One of the allowed methods:
GET
HEAD
POST
The only allowed headers which can will be set manually:
Accept
Accept-Language
Content-Language
Content-Type
DPR
Downlink
Save-Data
Viewport-Width
Width
The only allowed values for the Content-Type
header:
application/x-www-form-urlencoded
multipart/form-data
text/plain
No event listeners are registered on any XMLHttpRequestUpload
object used in a request (these are accessed using the property).
No object is used in a request.
Preflighted requests first send a HTTP request by the OPTIONS
method to a resource on an other domain, to determine if an actual request is safe to send.
Credentialed requests allow to send HTTP cookies and HTTP Authentication information (by default browsers will not send credentials).
When responding to a credentialed request, a server must specify an origin in a value of the Access-Control-Allow-Origin
header, instead of specifying the '*'
wildcard.
This section describes headers that a client may set when issuing HTTP requests in order to make use of the CORS feature. These headers are set by a browser when making requests to a server. Developers using cross-site XMLHttpRequest
capability do not have to set any CORS headers programmatically.
The Origin
header indicates an origin of a cross-site access request or preflight request. The origin
parameter is a URI indicating a server from which the request initiated. It does not include any path information, but only a server name.
The Access-Control-Request-Method
is used when issuing a preflight request to let a server know what HTTP method will be used when an actual request is made.
The Access-Control-Request-Headers
header is used when issuing a preflight request to let a server know what HTTP headers will be used when an actual request is made.
This section describes HTTP response headers that a server sends back for access control requests as defined by the CORS specification.
The Access-Control-Allow-Methods
header specifies a method or methods allowed when accessing a resource. This is used in response to a preflight request.
The Access-Control-Allow-Headers
header is used in response to a preflight request to indicate which HTTP headers can be used when making the actual request.
The Access-Control-Expose-Headers
header lets a server whitelist headers that browsers are allowed to access. By default, browsers have access to only the 7 CORS-safelisted response headers:
Cache-Control
Content-Language
Content-Length
Content-Type
Expires
Last-Modified
Pragma
The Access-Control-Max-Age
header indicates how long results of a preflight request can be cached.
Credentials are cookies, authorization headers or TLS client certificates.
If a victim's network location works as a kind of authentication, you can use a victim’s browser as a proxy to bypass IP-based authentication and access applications within an internal network.
In terms of impact this is similar to DNS rebinding.
Suppose an application that rigorously employs HTTPS also whitelists a trusted subdomain that is using plain HTTP. For instance, when an application receives the following request:
An application responds with:
In such case, an attacker who is in a position to intercept a victim's traffic can exploit the CORS configuration to compromise a victim's interaction with the application. The attack involves the next steps:
A victim user makes any plain HTTP request
An attacker injects a redirection to http://trusted-subdomain.vulnerable-website.com
A victim's browser follows the redirect
An attacker intercepts the plain HTTP request, and returns a spoofed response containing a CORS request to https://vulnerable-website.com
A victim's browser makes the CORS request, including the http://trusted-subdomain.vulnerable-website.com
as an origin
An application allows the request because the origin is whitelisted; requested sensitive data is returned in the response
An attacker's spoofed page can read the sensitive data and transmit it to any domain under their control
The attack is effective even if a vulnerable application is otherwise robust in its usage of HTTPS, with no HTTP endpoint and all cookies flagged as secure.
Most servers use basic string operations to verify the Origin
header, but some parse it as a URL instead. This allows you to exploit the browser's tolerance for unusual characters in domain names.
In Safari, https://website.com%60.attacker.com/
is a valid URL. If a CORS request originating from that URL, the Origin
header will look like next one:
A server may parse this header as https://website.com
omitting the `.attacker.com/
part.
Chrome and Firefox supported the _
character in subdomains, that can be used instead of `
to exploit Firefox and Chrome users.
You can also try to use other approaches to break a validation:
References:
Even "correctly" configured CORS establishes a trust relationship between two origins. If an application trusts an origin that is vulnerable to XSS, an attacker can inject JavaScript to retrieve sensitive information from the application using CORS.
Given the following request:
If an application responds with:
An attacker who finds an XSS vulnerability on subdomain.vulnerable-website.com
could use that to retrieve an API key using a URL like: https://subdomain.vulnerable-website.com/?xss=<script>cors-stuff-here</script>
No browsers actually support a space-separated list of origins (the specification suggests this):
Additionally, a wildcard does not allow you to trust all subdomains:
There is only wildcard origin '*'
.
For instance, an application receives the following request:
and responds with:
If the response contains any sensitive information such as an API key or CSRF token, you can retrieve this by placing the following script on your resource:
If an application reflects the Origin
header without even checking it for illegal characters like , you have a HTTP header injection vulnerability against IE/Edge users, because IE and Edge view \r (0x0d)
as a valid HTTP header terminator:
Internet Explorer sees the response as:
This is not directly exploitable because there is no way for an attacker to make someone's browser send such a malformed header, but you can manually craft this request and a server-side cache may save the response and serve it to other people. The current payload will change the page's character set to UTF-7, which is notoriously useful for creating XSS vulnerabilities.
The specification for the Origin
header supports the value null
. Browsers might send the value null
in the Origin
header in various unusual situations:
Cross-site redirects
Requests from serialized data
Request using the file:
protocol
Sandboxed cross-origin requests
Some applications might whitelist the null
origin to support local development of the application.
For instance, application receives the following request:
and responds with:
In such case, an attacker can use various tricks to generate a cross-domain request containing the value null
in the Origin
header. This will satisfy the whitelist, leading to cross-domain access. For example, this can be done using a sandboxed iframe cross-origin request of the form:
The CORS specification instructs developers specify Origin
in the Vary
response header whenever Access-Control-Allow-Origin
headers are dynamically generated.
Suppose an application reflects the contents of a custom header without encoding (reflected XSS in a custom HTTP header):
Without CORS, this is impossible to exploit as there is no way to make someone's browser send the X-User-id
header cross-domain.
With CORS, you can make them send this request. By itself, that is useless since the response containing injected JavaScript will not be rendered. However, if Vary: Origin
has not been specified the response may be stored in the browser's cache and displayed directly when the browser navigates to the associated URL.
The Access-Control-Allow-Origin
specifies either a single origin (), which tells browsers to allow that origin to access a resource. For requests without credentials - the '*'
wildcard, to tell browsers to allow any origin to access a resource.
If a server specifies a single origin rather than the '*'
wildcard, a server should also set Origin
in the response header - to indicate to clients that server responses will differ based on the value of the Origin
request header.
When a request's credentials mode () is include
, browsers will only expose a response to frontend JavaScript code if the Access-Control-Allow-Credentials
value is true.
Since you can't use wildcard in Access-Control-Allow-Origin
when credentials flag is true, check , many applications programmatically generate the Access-Control-Allow-Origin
header based on the user-supplied Origin
value. If you see a HTTP response with any Access-Control-*
headers but no origins declared, this is a strong indication that an application will generate the header based on a user input. Other applications will only send CORS headers if they receive a request containing the Origin
header, making associated vulnerabilities extremely easy to miss.
That might sound pretty simple, but immense numbers of developers forget, including the .