SOP and CORS

Visual representation

What is Same-Origin Policy (SOP) ?

One of the web browser security mechanism. SOP restricts javascript on site A from accessing site B based on preset conditions. The conditions can be ports, url, domain, or scheme.

What is Cross-Origin Resource Sharing (CORS) ?

CORS allows javascript on a web browser to access resources from other sites.
CORS headers can be noticed via Access-Control-* from request/response.

CORS attack is very similar to XSS attack, the difference is CORS is intended to reach out to other domains and XSS is intended for the same domain.


Access-Control-Request-Method: GET
Access-Control-Request-Headers: content-

Access-Control-Allow-Origin: http://127.0.0.1:5000
Access-Control-Allow-Headers: Content-Type
Access-Control-Allow-Methods: POST, GET, OPTIONS
Access-Control-Max-Age: 86400
Access-Control-Allow-Credentials: true

Out of all those two headers are significantly important
Access-Control-Allow-Origin : * --> Indicates that the target website will trust requests from any origin.

Access-Control-Allow-Credentials : True --> Indicates the browser will send user's cookies. Meaning attackers can ride the victim's session.


Example using a premature Flask python scripts.

app.py --> https://raw.githubusercontent.com/LifeTimeScriptKiddie/LifeTimeScriptKiddie.github.io/main/notes/SOP_CORS/app.py

Snapshot from 127.0.0.1:5000. Sending a Get request to http://127.0.0.1:5000/data.
Observe there is no 'Origin' header in the request. This is a request that is reaching out to resource that is on the same network.

Script is as below. The get request is reaching out to http://127.0.0.1:5000/data.
If this website is properly configured ( meaning if I knew how to build the website properly with some extra time), browsers could reach out to sub directories under /data.

    <script>
        function fetchData() {
            fetch('http://127.0.0.1:5000/data')
            .then(response => response.json())
            .then(data => {
                document.getElementById('result').textContent = data.message;
            })
            .catch(error => {
                console.error('Error fetching data:', error);
            });
        }

...


@app.route('/data')
def data():
    return jsonify(message="This is data from the same origin.")



Let's take a look at cors_app.py

cors_app.py --> https://raw.githubusercontent.com/LifeTimeScriptKiddie/LifeTimeScriptKiddie.github.io/main/notes/SOP_CORS/cors_app.py

Notice Origin and Access-Control* headers.


By changing Origin header,

Notice Options header. This is a preflight request to check what options are authorized.
Caveat: Once again, this is man made feature. Meaning, it is up to the developer to set it up or not.

@app.route('/api/data', methods=['GET', 'POST', 'OPTIONS'])                                                                                                  
def api_data():
    if request.method == 'OPTIONS':                                           
        response = make_response('', 204)
        response.headers['Access-Control-Allow-Origin'] = 'http://127.0.0.1:5000'
        response.headers['Access-Control-Allow-Headers'] = 'Content-Type'
        response.headers['Access-Control-Allow-Methods'] = 'POST, GET, OPTIONS'
        response.headers['Access-Control-Max-Age'] = '86400'
        response.headers['Access-Control-Allow-Credentials'] = 'true'
        return response

    origin = request.headers.get('Origin')
    if origin == 'http://127.0.0.1:5000':
        if request.method == 'GET':
            response = make_response(jsonify(message="This is a GET request from a different origin with custom CORS headers."))
        elif request.method == 'POST':
            data = request.json
            response = make_response(jsonify(message="This is a POST request from a different origin with custom CORS headers.", data=data))
         
        response.headers['Access-Control-Allow-Origin'] = origin
        response.headers['Access-Control-Allow-Headers'] = 'Content-Type'
        response.headers['Access-Control-Allow-Methods'] = 'POST, GET, OPTIONS'
        response.headers['Access-Control-Max-Age'] = '86400'
        response.headers['Access-Control-Allow-Credentials'] = 'true'
        return response
    else:
        return jsonify(message="CORS policy does not allow this origin."), 403 

References

https://portswigger.net/web-security/cors/same-origin-policy
https://portswigger.net/web-security/cors#:~:text=Cross-origin resource sharing (CORS) is a browser mechanism,outside of a given domain.