Same origin Policy and CORS (Cross-origin resource sharing)

Same-origin policy

What is it?

The same-origin policy is a security measure standardized among browsers. The “origin” mostly refers to a “domain”. It prevents different origins from interacting with each other, to prevent attacks such as Cross Site Request Forgery.

How does a CSRF attack work?

Browsers allow websites to store information on a client’s computer, in the form of cookies. These cookies have some information attached to them, like the name of the cookie, when it was created, when it will expire, who set the cookie etc. A cookie looks something like this:

Cookie: cookiename=chocolate;  Domain=.bakery.com; Path=/ [//  ;otherDdata]

So this is a chocolate cookie, which should be accessible from http://bakery.com and all of its subdomains.

This cookie might contain some sensitive data. In this case, that data is… chocolate. Highly sensitive, as you can see.

So the browser stores this cookie. And whenever the user makes a request to a domain on which this cookie is accessible, the cookie would be sent to the server for that domain. Happy server.

This is a good thing. Super cool way for the server to store and retrieve information on and from the client-side.

But the problem is that this allows http://malicious-site.com to send those cookies to http://bakery.com, without the user knowing! For example, consider the following scenario:

# malicious-site.com/attackpage

var xhr = new XMLHttpRequest();
xhr.open('GET', 'http://bakery.com/order/new?deliveryAddress="address of malicious user"');
xhr.send();

If you visit the malicious site, and the above code executes, and same-origin policy was not there, the malicious user would place an order on behalf of you, and get the order at his place… and you might not like this.

This happened because your browser sent your chocolate cookie to http://bakery.com, which made http://bakery.com think that you are making the request for the new order, knowingly. But you aren’t.

This is, in plain words, a CSRF attack. A forged request was made across sites. “Cross Site Request Forgery”. And it would not work, thanks to the same-origin policy.

How does Same-origin policy solve this?

It stops the malicious-site.com from making requests to other domains. Simple.

In other words, the browser would not allow any site to make a request to any other site. It would prevent different origins from interacting with each other through such requests, like AJAX.

However, resource loading from other hosts like images, scripts, stylesheets, iframes, form submissions etc. are not subject to this limitation. We need another wall to protect our bakery from malicious site, by using CSRF Tokens.

CSRF Tokens

As stated, malicious site can still do something like this without violating the same-origin policy:

<img src="http://bakery.com/order/new?deliveryAddress="address of malicious user""/>

And the browser will try to load an image from that url, resulting in a GET request to that url sending all the cookies. To stop this from happening, we need some server side protection.

Basically, we attach a random, unique token of suitable entropy to the user’s session, store it on the server, and also send it to the client with the form. When the form is submitted, client sends that token along with the request, and server verifies if that token is valid or not.

Now that we have done this, and malicious website sends the request again, it will always fail since there is no feasible way for the malicious website to know the token for user’s session.


CORS

When required, the policy can be circumvented, when cross site requests are required. This is known as CORS. Cross Origin Resource Sharing.

This works by having the “domains” tell the browser to chill, and allow such requests. This “telling” thing can be done by passing a header. Something like:

Access-Control-Allow-Origin: //comma separated allowed origins list, or just *

So if http://bakery.com passes this header to the browser, and the page creating the request to http://bakery.com is present in the origin list, then the browser will let the request go, along with the cookies.

There are rules according to which the origin is defined1. For example, different ports for the same domain are not the same origin. So the browser might decline this request if the ports are different. As always, our dear Internet Explorer is the exception to this. IE treats all ports the same way. This is non-standard and no other browser behaves this way. Do not rely on this.


JSONP

JSON with Padding is just a way to circumvent same-origin policy, when CORS is not an option. This is risky and a bad practice. Avoid using this.

What this technique involves is making a request to the other server like following:

<script src="http://badbakery.com/jsonpurl?callback=cake"></script>

Since same-origin policy does not prevent this2 request, the response of this request will be loaded into the page.

This url would most probably respond with JSON content. But just including that JSON content on the page is not gonna help. It would result in an error, ofcourse. So http://badbakery.com accepts a callback parameter, and modifies the JSON data, sending it wrapped in whatever is passed to the callback parameter.

So instead of returning,

{ user: "vuln", acc: "B4D455" }

which is invalid JavaScript throwing an error, it would return,

cake({user: "vuln", acc:"B4D455"});

which is valid JavaScript, it would get executed, and probably get stored somewhere according to the cake function, so that the rest of the JavaScript on the page can use the data.

This is mostly used by APIs to send data to other domains. Again, this is a bad practice, can be risky, and should be strictly avoided.

Why is JSONP bad?

First of all, it is very much limited. You can’t handle any errors if the request fails (at-least not in a sane way). You can’t retry the request, etc.

It also requires you to have a cake function in the global scope which is not very good. May the cooks save you if you need to execute multiple JSONP requests with different callbacks. This is solved by temporary functions by various libraries but is still a hackish way of doing something hackish.

Finally, you are inserting random JavaScript code in the DOM. If you aren’t 100% sure that the remote service will return safe cakes, you can’t rely on this.


References

1. https://developer.mozilla.org/en-US/docs/Web/Security/Same-origin_policy#Definition_of_an_origin

2. https://www.w3.org/Security/wiki/Same_Origin_Policy#Details

Other worthy reads

http://scarybeastsecurity.blogspot.dk/2009/12/generic-cross-browser-cross-domain.html

https://www.rfc-editor.org/rfc/rfc3986 (sorry :p)

https://developer.mozilla.org/en-US/docs/Web/Security/Same-origin_policy

https://www.owasp.org/index.php/Cross-Site_Request_Forgery_(CSRF)

Leave a Comment