217

The way I understand it, if a client-side script running on a page from foo.com wants to request data from bar.com, in the request it must specify the header Origin: http://foo.com, and bar must respond with Access-Control-Allow-Origin: http://foo.com.

What is there to stop malicious code from the site roh.com from simply spoofing the header Origin: http://foo.com to request pages from bar?

Jay Lamont
  • 2,867
  • 3
  • 17
  • 14
  • 2
    I believe the point is that the original domain the page is served from (here, `foo.com`) has to provide the `Access-Control-Allow-Origin` header or else the browser doesn't allow the request to `bar.com`. – Chris Hayes Jan 11 '14 at 03:28
  • 2
    Reading through [this post](http://www.html5rocks.com/en/tutorials/cors/) really helped me out in my understanding of the cors process between the browser, origin server, and target server. http://www.html5rocks.com/en/tutorials/cors/ – brendonparker Jan 11 '14 at 03:30
  • 7
    @ChrisHayes That's not how CORS works at all. You can read up on this a bit more by looking at [the spec](http://www.w3.org/TR/cors/), or [this great MDN wiki page on the subject](https://developer.mozilla.org/en-US/docs/HTTP/Access_control_CORS). – Ray Nicholus Jan 11 '14 at 03:52
  • 1
    @brendonparker Yes, that's a great article. That author answers a lot of CORS questions on SO, and also maintains [enable-cors.org](http://enable-cors.org/). – Ray Nicholus Jan 11 '14 at 03:54
  • ...speak of the devil ;-) – Ray Nicholus Jan 11 '14 at 03:54
  • 5
    @RayNicholus Interesting, I was clearly way off. Thanks for the links. Judging by the votes on my comment I'm not the only one suffering under this delusion. I hope those two come back and learn (and remove their votes!). – Chris Hayes Jan 11 '14 at 04:06
  • @ChrisHayes CORS is an often misunderstood topic. Before I began working on [Fine Uploader](http://fineuploader.com), I honestly didn't know the first thing about CORS or the [same origin policy](http://tools.ietf.org/html/rfc6454#section-5). More on the SOP: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Same_origin_policy_for_JavaScript. – Ray Nicholus Jan 11 '14 at 04:11

5 Answers5

227

Browsers are in control of setting the Origin header, and users can't override this value. So you won't see the Origin header spoofed from a browser. A malicious user could craft a curl request that manually sets the Origin header, but this request would come from outside a browser, and may not have browser-specific info (such as cookies).

Remember: CORS is not security. Do not rely on CORS to secure your site. If you are serving protected data, use cookies or OAuth tokens or something other than the Origin header to secure that data. The Access-Control-Allow-Origin header in CORS only dictates which origins should be allowed to make cross-origin requests. Don't rely on it for anything more.

Joshua Taylor
  • 84,998
  • 9
  • 154
  • 353
monsur
  • 45,581
  • 16
  • 101
  • 95
  • 5
    This makes a lot of sense. If the browser doesn't allow JavaScript to override the Origin header, then there is no problem. If you're executing requests from outside the browser, you're not going to have the cookies. I guess I was confused because in all of the docs I was reading, nowhere did it say explicitly that the Origin header *couldn't* be overridden. Thanks! – Jay Lamont Jan 12 '14 at 00:39
  • 67
    If someone wants to spoof something, then they can do so. Using pretty much any scripting language they can construct http requests. Perl and Python have http libraries which make this pretty easy. The libraries store and send cookies, let you add arbitrary headers, and give plenty of debugging information. So the CORS headers are just to make it harder for malicious javascript on a forum you read to do something nasty to your bank account on another domain when you're logged into both in your browser. – Mnebuerquo Jan 15 '14 at 15:30
  • 12
    And just to clarify, the malicious user could simply spawn a browser instance that was patched to allow them manual control over the Origin header, and then perfectly impersonate a normal user, cookies, AJAX and all. – Jordan Rieger Oct 01 '14 at 18:35
  • 12
    "Browsers are in control of setting the Origin header, and user's can't override this value." I'm sure it's very easy to use a tool like Fiddler2 or Charles to modify the headers once the request leaves the browser. – Asa Jan 13 '15 at 06:51
  • See http://stackoverflow.com/questions/40835863/in-the-respective-of-security-is-it-meaningful-to-allow-cors-for-specific-domai/40836602#40836602 The point is: browsers set the Origin header based on what they know the real origin to be. So even if you manage to change an Origin header that’s sent out over the network, that won’t matter—browsers are going to ignore that & continue checking against what they know to be the actual origin. Browsers are where cross-origin/CORS restrictions are enforced, & you’re not going to trick a browser into thinking an app is running from a different origin. – sideshowbarker Feb 20 '17 at 02:39
  • So basically all CORS does is prevent cross-site scripting attacks for sites that use session cookies and for attack code that does not use JSONP... And for that we are stuck with double the latency on all POST requests or requests that add an Authorization header (because of the OPTIONS pre-flight check)... Mmmm this does not really sound like it's worth the overhead but I guess we're stuck with it now. – Stijn de Witt Oct 05 '17 at 17:38
  • 6
    *the malicious user could simply spawn a browser instance that was patched to allow them manual control over the Origin header* If you have access to the machine to the point where you can 'simply spawn a patched browser instance' (doesn't actually sound that simple to me), why not just directly read the cookies from disk? They are stored in plain text you know. In real life, cross-site scripting is a real threat, whereas your attack scenario is just contrived and impractical. – Stijn de Witt Oct 05 '17 at 17:42
  • 1
    To clarify for the readers - HTTP headers can easily be spoofed and they can also be crafted to contain the necessary cookies. Nothing in the post or comments section indicates otherwise – Chris Maggiulli Feb 07 '18 at 11:31
  • @StijndeWitt You got it exactly backward. CORS does not prevent anything, it allows things. – curiousguy Apr 17 '20 at 22:08
  • @curiousguy By "CORS", most people are referring to browsers' blanket policy of denying scripts access to cross-site requests when the requested server doesn't insert special headers. Cross-Origin Request Security: *security* prohibits things. You then need to go in with a special header to allow things, on the off-chance the API you're trying to access is administrated by someone co-operative. – JamesTheAwesomeDude Mar 12 '21 at 21:56
  • In a nutshell: an *attacker* may disregard `Allow-Origin` headers (or lack thereof), but this is irrelevant because **an attacker doesn't have the user's privileged credentials**. – JamesTheAwesomeDude Mar 12 '21 at 21:58
60

TLDR: There's nothing stopping malicious code from spoofing the origin. When that happens, your server will never know about it and will act upon the requests. Sometimes those requests are expensive. So don't use CORS in place of any type of security.


I've been playing around with CORS recently, and I've asked myself the same question. What I've found is that the browser may be smart enough to know a spoofed CORS request when it sees one, but your server isn't as smart.

The first thing I found was that the Origin header is an HTTP forbidden header name that cannot be modified programmatically. Which means you can modify it in about 8 seconds using Modify Headers for Google Chrome.

To test this, I set up two Client domains and one Server domain. I included a CORS whitelist on the Server, which allowed CORS requests from Client 1 but not from Client 2. I tested both clients, and indeed Client 1's CORS requests succeeded while Client 2's failed.

Then I spoofed Client 2's Origin header to match Client 1's. The Server received the spoofed Origin header, and successfully passed the whitelist check (or failed if you're a glass-half-empty kind of guy). After that, the Server performed dutifully by consuming all the resources that it was designed to consume (database calls, sending expensive emails, sending even more expensive sms messages, etc.). When that was done, the server happily sent the spoofed Access-Control-Allow-Origin header back to the browser.

The documentation I've read states that the Access-Control-Allow-Origin value received must match the Origin value sent in the request exactly. They did match, so I was surprised when I saw the following message in Chrome:

XMLHttpRequest cannot load http://server.dev/test. The 'Access-Control-Allow-Origin' header has a value http://client1.dev that is not equal to the supplied origin. Origin http://client2.dev is therefore not allowed access.

The documentation I read doesn't seem to be accurate. Chrome's network tab clearly shows both the request and response headers as http://client1.dev, but you can see in the error that Chrome somehow knows the real origin was http://client2.dev and correctly rejects the response. Which doesn't matter at this point because the server had already accepted the spoofed request and spent my money.

Nocturno
  • 9,579
  • 5
  • 31
  • 39
  • 1
    Spoofed Request --> But if you use fiddler for intercepting the response, before it reaches chrome, then you can see the data sent back from the CORS server, right?! – Legends May 20 '16 at 09:22
  • 1
    Browsers do CORS checks against what they know the be the actual origin of a resource. They don’t do CORS checks against the value of the `Origin` header. So even if you manage to change the value of the `Origin` header the gets sent over the network, it’s not going to matter to browsers—they’re going to continue to check against the real origin. – sideshowbarker Nov 28 '16 at 02:08
  • 2
    @Nocturno, thank you for the example. Let me just add my observation. CORS relates to browser safety features. If a safe browser is modified from its pristine state, that could be interpreted as the browser possibly lacks a safety feature. – Luka Žitnik Mar 04 '17 at 08:09
  • 1
    Brilliant answer – William B Jul 20 '17 at 06:08
  • 23
    Not brilliant at all. It completely misses the point of CORS. If you are in the position to intercept requests originating from the user's machine, you can just read their cookies, install keyloggers, virusses and all those other real threats. CORS is there to protect honest users logged into site A from a malicious script that somehow got injected to site B. The script on site B (which could be a snippet of Javascript in a forum post that was not escaped correctly by site B) performs actions on site A under the user's account (e.g. delete stuff etc), using the session cookie from site A. – Stijn de Witt Oct 05 '17 at 17:48
  • 6
    This is called cross-site scripting and without CORS could be done without ever needing to gain control over the user's machine. That's the whole point. No control over the user's machine was needed because when making requests to site A the browser used to automatically add the session cookie to the request so it looked like a valid request from the user itself when in fact it was coming from a script on some other site. Same-Origin policy prevents it and CORS is used to whitelist domains that should be granted access even though they are on a different origin. – Stijn de Witt Oct 05 '17 at 17:50
  • 2
    @StijndeWitt Actually the point I was making was to answer the original question by showing that there's nothing stopping malicious code from spoofing the origin. And when that happens, your server will never know about it and will send the requested response back to it, and sometimes those requests/responses are expensive. So don't use CORS in place of any type of security. – Nocturno Oct 05 '17 at 23:28
  • 3
    @Nocturno Yeah I was maybe a bit too crude, sorry about that. Your original point stands. Same-Origin policy is a browser security feature and CORS is a mechanism to weaken that security by whitelisting some domains. OP needs to understand that spoofing the Origin header is not really viable as an 'attack' since it does not bring you anything that can't be had with e.g. curl. – Stijn de Witt Oct 06 '17 at 11:21
  • 5
    @Nocturno I think your opening statement is a bit misleading. `There's nothing stopping malicious code from spoofing the origin` -> Yes there is, javascript cannot set `Origin`. Yes, a user can modify their browser/use fiddler to change origin, but that's not what CORS is defending against; _attacker controlled websites_ can't change Origin, which is all that matters. – RJFalconer Jun 07 '18 at 13:53
  • Nocturno, You sent a "simple" request, so after the response with the access control header did it get declined and the code run. If you want avoid this you can check for headers that are part of the "preflight" request headers and block if non existent and send them in your own JS code when needed. See: https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#Preflighted_requests and see the headers you could give your own requests and confirm for on the end side. This makes the browser send an "OPTIONS" request first, without executing your script, which you can block all "simple" requests. – Jesse Sep 26 '18 at 17:09
32

Just a humble wrap up:

Q: Is Same Origin Policy (SOP) enforced only by browsers?
A: Yes. For all calls you make inside a browser, the SOP is definitely applied by the browser. Server might or might not check the origin of the request.

Q: If a request doesn't comply with SOP, does the browser block it?
A: No, it's beyond authority of browsers. Browsers just send cross origin requests and wait for the response to see if the call is signaled legit by server through Access-Control-* headers . If server doesn't send back Access-Control-Allow-Origin header, doesn't echo back the origin of caller, or doesn't send back * in the header, then all the thing a browser will do is refraining from providing the response to the caller.

Q: Does it mean I cannot spoof Origin?
A: In browser and using scripting, you cannot override Origin as it's in the control of browser. However, if you want to hack yourself, you can tamper the calls coming out of YOUR browser using browser extensions or other tools you install on your machine. You can also issue HTTP calls using curl, Python, C#, etc and alter the Origin header to trick servers.

Q: So if I can trick server by altering Origin, does it mean CORS is not secure?
A: CORS per se is silent about security - i.e. authentication and authorization of requests. It's up to servers to inspect requests and authenticate/authorize them by any mechanism they work with such as cookies and headers. Having said that, it can protect us a bit more in case of attacks like XSS:

Example: Let's say you've logged in to your website and a malicious script attempts to send a request to your bank website to inquire your balance: a Reflected XSS attack. Your bank website trusts the credentials coming from (here on behalf of) your website so the request gets authenticated and a HTTP response aiming for the malicious code gets issued. If your bank website doesn't care about sharing its endpoints with other origins, it doesn't include Access-Control-Allow-Origin header in the response. Now, upon arrival of the request, the browser realizes that the request was a Cross Origins request, but the response doesn't show that the server was happy to share the resource (here the balance query endpoint) with your website. So it breaks the flow, hence the returned result will never reach to the malicious code.

andynil
  • 28,248
  • 2
  • 30
  • 26
Alireza
  • 10,237
  • 6
  • 43
  • 59
1

This topic being a little old but definitely helpful, I’ll add the following tips for anyone wondering if there’s any way to prevent an attacker to spoof the cors.

As said above there is NO WAY to prevent the Origin header from being spoofed.

However if you are for instance building an API returning data displayed publicly and want to avoid an attacker overflowing the server to retrieve all data you could do the following:

  • prevent global data request (a query that would return all data available at once)
  • setup a logger checking if a malicious user is editing or creating a script to send multiple fast subsequent requests. You could use a combination of IP address and other unique headers to try and achieve that.

If you want to secure a rest API, HMAC or Oauth2 are your best options (each having its own purpose).

But cors will always remain editable and should never be used to check the identity of requests emitters.

user3491125
  • 366
  • 1
  • 3
  • 13
1

While it is possible for a malicious website to spoof the Origin header in a request to trick a server into allowing CORS, there are a few ways that servers can protect against this.

One way is to use a technique called "origin whitelisting," where the server only allows requests from specific, trusted origins. This means that even if a malicious website spoofs the Origin header, the server will not allow the request unless the origin is on the whitelist.

Another way is to use signed tokens or cookies to authenticate requests. In this case, the server can verify that the request is coming from a trusted source by checking the signature or the cookie value. This makes it much more difficult for a malicious website to trick the server into allowing CORS.

It's also worth noting that CORS is just one layer of security for protecting against cross-site requests. There are other techniques and measures that web developers can use to further protect their sites from malicious requests.

Yehuda Schwartz
  • 3,378
  • 3
  • 29
  • 38