Same origin policy

Hi guys,

Just watched a few videos on browsers' same origin policy. I think I have the general idea, I think.... Let's say we have created a website, for argument sake, this website will be hosted on an Apache web server on my local machine, which will implement port forwarding.

From my understanding, I cannot write client side Javascript that will make a GET request or any request for that matter(POST etc,) to another website that is not of the same origin as my website.

When the user navigates to my page example(dot)com, the JS will be sent to their browser. This JS can make further requests, such as, if a user pressed a button or initiated any other event. This JS code will be limited to only making requests from the origin(example(dot)com). This makes sense, as our JS code could send the browsers cookies back to example(dot)com or any other server of the attackers choosing.

Well, if this is the case, how come we can make requests to APIs in our client side code? or... can we? Say, if I embed a script to make a get request to exampleapi(dot)com, will the JS sent to the client be able to run this code? or will the request be blocked by the browser?

If so, does this mean, we have to make the API requests server side at example(dot)com and then send the fetched data from our server back down the wire to our requesting clients?

Thanks
Last edited on
The "same origin" policy is required, because otherwise a rogue web-site could instruct your browser to perform an arbitrary request (e.g. via XMLHttpRequest) to another, completely unrelated web-site. Now consider this case: If you are logged-in on that other web-site, and therefore your browser will send your session cookie along with the request, it would seem to the other web-site like a legitime request from your personal account. But you don't even know about that request, because the rogue web-site is doing all this "in the background".

The rogue web-site would thus be able to "control" the other web-site in your name, i.e. with your session/credentials 😨

To avoid this kind of attack, all modern browsers restrict XMLHttpRequest (and similar techniques) via "same origin" policy, i.e. the scheme, host name and port number of the target URL must match to the web-site that is issuing the request. Otherwise, the request is blocked.

Blocking happens client-side, in the browser. Note that "normal" hyperlinks and HTML forms are not subject to "same origin" policy!

If so, does this mean, we have to make the API requests server side at example(dot)com and then send the fetched data from our server back down the wire to our requesting clients?

The "same origin" policy can be relaxed, if required, by using a feature called Cross-origin resource sharing (CORS):
https://en.wikipedia.org/wiki/Cross-origin_resource_sharing

Note: CORS requires the target web-site to announce from which web-sites (origins) it is expecting incoming requests!

You can't implement CORS on the source (requesting) web-site alone, and that is for good reasons.
Last edited on
Ah okay, starting to click, but, with that in mind, and in regards to script tags, we can't make http requests to any site that isn't of the same origin(not using CORS). Is this why generally requests to other APIs are handled server side and not client side? i.e when a user clicks on a get weather button, the server will make a request to the weather api site on behalf of the user and serve it back to the user on behalf of the weather api site?

Last edited on
Yes, if you want to send a request to an API, e.g. by using XMLHttpRequest, then that API must be reachable under the same origin (scheme, host name and port number) as the web-site that is issuing the request. Either that, or CORS must be used, by the API server, to explicitly allow incoming requests from your web-site. Again, "normal" hyperlinks or HTML forms are not restricted by the "same origin" policy.

Surely, you can use something like proxy_pass to transparently forward the request from your web-server to the API server:
https://docs.nginx.com/nginx/admin-guide/web-server/reverse-proxy/

1
2
3
location /api/ {
    proxy_pass http://api.third-party.com/;
}

Above config transparently forwards any request to "http://www.your-server.com/api/<path>" to "http://api.third-party.com/<path>".

But, of course, it means that your server now becomes a bottleneck for all API access! (from your web-site)
Last edited on
That's starting to click :) A quick follow up, also, sorry about the late response, the weekend was hectic.

If you could, tell me if I'm correct, pretty much correct or wrong with my understanding. I'll break it into quick subsections on what the same origin policy essentially does.

A) The browser stops Javascript code retrieved from a website making GET requests to another website such as Facebook, because some websites store sessions(logged in), when you make a GET request from that website, you can capture the session cookie, which obviously is a no-no.

B) The browser also stops you from accessing the document and (most) code from inside iframes. By doing this, even if we create a malicious site that has a hidden iframe with Facebook or even a banking site, and assuming we are logged into that website in another tab or have a logged in session. This prevents us from accessing critical data inside this iframe's document such as submitting a form or even stealing the cookies.

C) There are ways around this, by using HTML elements, such as; <form>,<img> tags.

is this correct?
A) The browser stops JavaScript code retrieved from a website making GET requests to another website such as Facebook, because some websites store sessions(logged in), when you make a GET request from that website, you can capture the session cookie, which obviously is a no-no.

More precisely, if Javascript code on web-site X would be allowed to make arbitrary requests (e.g. via XMLHttpRequest) to an unrelated web-site Y, then your web-browser would send any cookies that were previously created by web-site Y along with those requests. Web-site X never "sees" those cookies! Nonetheless, if the cookie contains something like a session ID, which is a typical use-case for cookies, then to web-site Y those requests would seem like legitimate requests originating from your session (user account), even though the request was in fact created by the JavaScript code from web-site X – and therefore is fully under the control of web-site X.

Example: The JavaScript code on the web-site "evil.com" would be able to create new posts at the "cplusplus.com" forum under your user account – provided that you have an active session at "cplusplus.com". This could happen totally hidden, when you visit "evil.com".

I think this is not limited to GET requests, but effects, e.g., POST requests too.

The same origin policy prevents this kind of cross-site request forgery (CSRF) attacks. There are other anti-CSRF techniques too.

B) The browser also stops you from accessing the document and (most) code from inside iframes. By doing this, even if we create a malicious site that has a hidden iframe with Facebook or even a banking site, and assuming we are logged into that website in another tab or have a logged in session. This prevents us from accessing critical data inside this iframe's document such as submitting a form or even stealing the cookies.

Web-sites set the X-Frame-Options header to indicated whether embedding in <iframe>s on other web-sites should be allowed.

https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options

C) There are ways around this, by using HTML elements, such as; <form>,<img> tags.
<img> and <form> are not restricted by the same origin policy. I think this is mostly for "legacy compatibility" reasons.

An API server could easily "block" GET or POST requests generated via HTML <form>, simply by requiring a custom header in the request. Requests lacking the custom header would be discarded by the server. With HTML <form> it is not possible to add a custom header to the requests! XMLHttpRequest can add custom headers to the request, but with XMLHttpRequest the same origin policy kicks in 😏
Last edited on
A) now makes perfect sense :). example B) sort of makes sense, so websites such as Facebook or more pertinently a banking website, could deny the ability for their websites to be embedded on a particular site. But, if and only if, the website didn't deny the ability to embed their site inside of another(evil.com), then theoretically, evil.com could access data such as the session id or more much like in example A). Although..... modern browsers do not let you access important parts or all of the code(HTML,JS,even CSS) inside of the embedded iframe, right?

I feel example C is probably the most crucial part to remember and I would guess this is also the most vulnerable part of a websites infrastructure. When I get a little bit more experienced with web dev, example C will probably become clearer. I also here there is a way for a server to generate tokens that prevents or renders this type of attack improbable. Although, I have no idea how sending a custom token to the client would prevent evil.coms JS from reading that custom token.
Last edited on
When I get a little bit more experienced with web dev, example C will probably become clearer. I also here there is a way for a server to generate tokens that prevents or renders this type of attack improbable. Although, I have no idea how sending a custom token to the client would prevent evil.coms JS from reading that custom token.

When web-site X sends HTML code or JavaScript code to the client, it can include a "token" (e.g. pseudo-random number) that is unique to the user and that expires after a short time. Any request back to web-site X is required to include that "token", e.g. as a request header or as a query parameter. The server checks the validity of the "token" and refuses the request, if the "token" is missing or invalid.

Now, if web-site Y, or, more precisely, the HTML or JavaScript code that was delivered to your web-browser from web-site Y, tries to issue a request to unrelated web-site X, then it cannot know the correct "token", so the request is probably going to fail.
Last edited on
Now, if web-site Y, or, more precisely, the HTML or JavaScript code that was delivered to your web-browser from web-site Y, tries to issue a request to unrelated web-site X, then it cannot know the correct "token", so the request is probably going to fail.


But could web-site Y not use some JS to find out the token that was sent to the client? That's one thing thats confused me with the token solution to these attacks.
But could web-site Y not use some JS to find out the token that was sent to the client? That's one thing thats confused me with the token solution to these attacks.

The web-site X will only reveal the correct "token", if the user is logged-in, i.e. the server checks the session cookie before returning the HTML or JavaScript code containing the "token". Web-site X can not get the token directly, because it doesn't know the session ID. Also it can not start a new session, because it doesn't know the credentials. Furthermore, web-site Y also can not get the "token" indirectly, e.g. by using XMLHttpRequest via JavaScript from within your browser, because the "same origin" policy (hopefully) prevents that. And even if the "same origin" policy didn't kick in, parsing the desired "token" out of the HTML/JavaScript code from the server would not be trivial.

(So, the "token" prevents the attack, or, at the very least, makes it much more difficult)

Note: The "token" can be used to protect access to resources that are not subject to the "same origin" policy (e.g. HTML <form> or <img>).
Conversely, if the server requires the "token" to be sent as a custom HTTP request header, then HTML <form> is completely ruled out.

See also:
https://portswigger.net/web-security/csrf/tokens
Last edited on
ah, yes, makes sense now. If you could, tell me if this is correct, it's probably a less precise rehash of what you've explained, but I'll nonetheless try give my understanding.

Website-Y for example, will create a form, set its action to user/maketransaction.php, the values will already be filled in such as current account, amount, etc.

Website-Y will also embed JS to submit the form when the page is loaded, but since it didn't make a request(for example make a GET request) to website-X, it does not have a token. SOP prevents website-Y from making a get request as it breaks SOP policy. Website-Y also cannot access any embedded content such as the documents inside of iframes, thus website-Y will not have a token.

But if a legit user, was to load the banking website(website-X), the user could request the transaction page, this will work as it does not break the SOP policy. A token will be sent to the client from the server and the client will then submit that form along with the token it was given, the token will then be verified once the form submission has been sent.

I think that's correct?
Yes, mostly.

In the most simple case, web-site X would simply include the "token" directly in the <form>, as a hidden input field, in the HTML code that it sends to the client. Meanwhile, web-site Y could make a "1:1" copy of that <form>, but does not know which "token" to fill in...

(Note: Web-site Y can not load the <form> HTML code with the already filled in "token" value from the web-site X — not even from within your browser, e.g. by using JavaScript and XMLHttpRequest, because the SOP usually prevents that)
Last edited on
In the most simple case, web-site X would simply include the "token" directly in the <form>, as a hidden input field, in the HTML code that it sends to the client. Meanwhile, web-site Y could make a "1:1" copy of that <form>, but does not know which "token" to fill in...


So the best chance would be for website-Y to simple guess or use a bruteforce style approach, but even at that It would be highly unlikely that website-Y would guess the correct token


Thanks Kigar :)
Topic archived. No new replies allowed.