CORS

CORS

in

Table of Contents

CORS

CORS Headers

All CORS headers start with “Access-Control”.
The most permissive headers would be:

Access-Control-Allow-Origin: unique_origin
Access-Control-Allow-Methods: *
Access-Control-Allow-Credentials: True
  • Access-Control-Allow-Origin: Describes which origins can access the response.
  • Access-Control-Allow-Credentials: Indicates if the request can include credentials (i.e cookies)
  • Access-Control-Expose-Headers: Instructs the browser to expose certain headers to JavaScript

From a security perspective, the most important headers when analyzing target applications for CORS vulnerabilities are Access-Control-Allow-Origin and Access-Control-Allow-Credentials.
It’s import to keep in mind that every endpoint and HTTP method can have different CORS headers depending on the actions that are allowed or disallowed.

HTTP Preflight Requests

Even through SOP only prevents javascript from reading reponse and not requests from being sent, there are some exceptions. Some requests require an HTTP preflight request (sent with the OPTIONS method), which determines if the subsequent browser request should be allowed to be sent.
Standard GET, HEAD, and POST requests don’t require preflight requests.
Other request methods, requests with custom HTTP headers, or POST requests with nonstandard content-types will require a preflight request.
A nonstandard content-type is defined as anything that is not:

  • application/x-www-form-urlencoded
  • multipart/form-data
  • text/plain If we consider XHR requests then a preflight request would look like:
    fetch("https://donkey.com",
    {
    method: 'post',
    headers: {
      "Content-type": "application/json;"
    }
    })
    

    i.e the only thing that changes is the content-type.

Checking for unsafe CORS headers

We can check if a request show be allowed to be sent on a route the application exposes, using the OPTIONS method.
It would like:

iwr -method "OPTIONS" https://cors-test.appspot.com/test

Or:

curl -X "OPTIONS" https://cors-test.appspot.com/test

The thing to keep in mind when checking CORS responses via the OPTIONS method is if the application replicates the Access-Control-Allow-Origin header in it’s response. If it does then we may have something of interest.

Access-Control-Allow-Credentials

  • Access-Control-Allow-Credentials only accepts a “true” value with the default being “false”. If this header is set to true, any request sent will include the cookies set by the site. This means that the browser will automatically authenticate the request.
  • When this header is present we can assume that cookies are used for session management.

Access-Control-Allow-Origin

As we have stated the only origins allowed to read a resource are those listed in the Access-Control-Allow-Origin header.
Access-Control-Allow-Origin can have three values:

  • “null”
    • Certain documents and files opened in the browser have a “null” origin and if the goal is to block other origins from sending requests to the target, removing the header is the most secure option. To repeat “null” is NOT a “secure” value.
  • ””
  • an origin

If the Access-Control-Allow-Origin header is set to a wildcard “””, all origins are allowed to read a resource from the remote server. This might seem like a vulnerable configuration, but this setting requires that *Access-Control-Allow-Credentials is set to false, which results in all requests being unauthenticated. If the header is set to an origin value, only that origin is allowed to read the resource and, if Access-Control-Allow-Credentials is set to true, include the cookies.

Access-Control-Allow-Origin only lets sites set a single origin. The header cannot contain wildcards (.donkey.com) or lists (donkey.com, hooves.com, hay.com). The workaround (which is insecure) for this is to dynamically set the *Access-Control-Allow-Origin header to the origin of the request, multiple origins can send requests with Cookies. While this is the standard solution the the single origin problem it effectively negates the purpose of SOP & CORS to begin with.
The way to test for this behavior is to add an origin header to a request and see if the origin is replicated by the webserver.

Origin: donkey.com

Local CORS Testing Server

If you’re familiar with python3’s http.server server module the code snippet below can be considered a http.cors_server.

from flask import Flask
from flask import request
from flask import make_response
app = Flask(__name__)

@app.route('/',methods=['GET','POST'])
def hello_world():
    print('Headers')
    print(request.headers)
    print("Method: ",request.method)
    print("Remote Addr: ",request.remote_addr)
    return 'CORS >_<!',200
@app.after_request
def after_request_func(response):
    origin = request.headers.get('Origin')
    if request.method == 'OPTIONS':
        response = make_response()
        response.headers.add('Access-Control-Allow-Credentials', 'true')
        response.headers.add('Access-Control-Allow-Headers', 'Content-Type')
        #response.headers.add('Access-Control-Allow-Headers', 'x-csrf-token')
        response.headers.add('Access-Control-Allow-Methods',
                            'GET, POST, OPTIONS, PUT, PATCH, DELETE')
        if origin:
            response.headers.add('Access-Control-Allow-Origin', origin)
    else:
        response.headers.add('Access-Control-Allow-Credentials', 'true')
        if origin:
            response.headers.add('Access-Control-Allow-Origin', origin)

    return response
### end CORS section
app.run('0.0.0.0',80)