What’s to stop malicious code from spoofing the “Origin” header to exploit CORS?
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?
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).
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.
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
‘Access-Control-Allow-Origin’ header has a value
that is not equal to the supplied origin. Origin
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.
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 browser can do is to refrain from providing the response to the caller.
Q: Does it mean I cannot spoof
A: In browser and using scripting, you cannot override
Origin as it’s in 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
C#, etc and alter the
Origin header to trick server.
Q: So if I can trick server by altering
Origin, does it mean
CORS is not secure?
CORS per say 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:
Let’s say you login to your website and a malicious script code attempts to send a request to your bank website to inquire your balance. Your bank website trusts the credentials coming from 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 one 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 and the returned result will never reach the malicious code.