I fail to understand certain things that I'd like to ask.
Scenario 1: I create a HTML/JavaScript website, in which I use AJAX to obtain HTML of Google.com, I'm met with infamous Cross-Domain issue (No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'null' is therefore not allowed access.)
Scenario 2: I enter www.google.com, I select Source Code in my context menu and I get the enter code.
Here are the questions:
What purpose does this message (and it's implications) have? How is Google protected from my devilish evil script whilst I can request the same website through browser? Isn't the request identical?
How are there origin differences between Scenario 1 and Scenario 2 when the source is browser, my laptop, my router, my ISP, the internet and then Google in both cases.
Why and who invented the way to discriminate against local scripts against browser itself, what purpose does it serve? If request would be malicious it would be equally malicious in both scenario's.
How does Google know what origin it comes from and how is it any different than me requesting their website through address bar? Once again, same exact origin.
Origin has nothing to do with your browser, laptop, router, ISP, etc. Origin is about the domain which is making the request. So if a script https://evil.com/devious.js is making an XHR request to http://google.com, the origin for the request is evil.com. Google never knows this. The user's browser checks the access control headers (the Access-Control-Allow-Origin you mentioned) and verifies that that script is permitted to make that request.
The purpose of all of this is to prevent a malicious script from (unbeknownst to a user) making requests to Google on their behalf. Think about this in the context of a bank. You wouldn't want any script from any other website being able to make requests to the bank (from your browser on your behalf). The bank can prevent this by disabling cross domain requests.
And to (1): When you open console on a google.com page, any requests you make have the origin google.com, which is why you are able to make these requests. This is different than the case I just mentioned, because the user would have to make a conscious effort to copy some malicious javascript, go to their bank's website, open up the console, and paste and run it (which many users would be suspicious of).
Websites are not typically stateless, they often store information on your machine, such as a session cookie that identifies the account you're logged into. If your browser didn't block cross origin requests that aren't explicitly allowed, you could be logged into gMail and then got to randomguysblog.org and a script on randomguysblog.org could make a POST request to gMail using your browser. Since you are logged in he could send emails on your behalf. Perhaps you're also logged in to your bank and randomguy decides to initiate a transfer for all your money to his account, or just look around and see how much money you have.
To respond to your questions individually:
What purpose does this message (and it's implications) have? How is Google protected from my devilish evil script whilst I can request the same website through browser? Isn't the request identical?
It's not Google directly who is protected, it's the users of your website who are also logged into Google. The request is identical, but the users browser won't even send the request assuming the server supports pre-flight, if the server doesn't support pre-flight requests, then it will send the request but won't allow the script that initiated it to see the response. There are other ways to send requests without seeing the response that don't use Ajax, such as by submitting a hidden form, which is why CSRF tokens are also needed. A CSRF token makes an action require two requests and a token in the response from the first request is needed to make the second one.
How are there origin differences between Scenario 1 and Scenario 2 when the source is browser, my laptop, my router, my ISP, the internet and then Google in both cases.
In scenario 2 the user is making both requests themselves so they must intend to make both request. In scenario 1 the user is only trying to access your website and your website is making the request to Google using their browser which they might not want your website to do.
Why and who invented the way to discriminate against local scripts against browser itself, what purpose does it serve? If request would be malicious it would be equally malicious in both scenario's.
The purpose is to protect browser users against malicious scripts. The malicious script can't access the response from Google in scenario 1. The user can, but it's not intended to protect users from attacking themselves.
How does Google know what origin it comes from and how is it any different than me requesting their website through address bar? Once again, same exact origin.
Google can could check the referrer header, but they don't actually need to know where the request is from. Google only needs to tell the browser where requests are allowed to come from and the user's browser decides whether or not to send the request to Google.
Related
I've been reading up on CORS and how it works, but I'm finding a lot of things confusing. For example, there are lots of details about things like
User Joe is using browser BrowserX to get data from site.com,
which in turn sends a request to spot.com. To allow this, spot has
special headers... yada yada yada
Without much background, I don't understand why websites wouldn't let requests from some places. I mean, they exist to serve responses to requests, don't they? Why would certain people's of requests not be allowed?
It would really appreciate a nice explanation (or a link to one) of the problem that CORS is made to solve.
So the question is,
What is the problem CORS is solving?
The default behavior of web browsers that initiate requests from a page via JavaScript (AKA AJAX) is that they follow the same-origin policy. This means that requests can only be made via AJAX to the same domain (or sub domain). Requests to an entirely different domain will fail.
This restriction exists because requests made at other domains by your browser would carry along your cookies which often means you'd be logged in to the other site. So, without same-origin, any site could host JavaScript that called logout on stackoverflow.com for example, and it would log you out. Now imagine the complications when we talk about social networks, banking sites, etc.
So, all browsers simply restrict script-based network calls to their own domain to make it simple and safe.
Site X at www.x.com cannot make AJAX requests to site Y at www.y.com, only to *.x.com
There are some known work-arounds in place (such as JSONP which doesn't include cookies in the request), but these are not a permanent solution.
CORS allows these cross-domain requests to happen, but only when each side opts into CORS support.
First, let's talk about the same origin policy. I'll quote from a previous answer of mine:
The same-origin policy was invented because it prevents code from one website from accessing credential-restricted content on another site. Ajax requests are by default sent with any auth cookies granted by the target site.
For example, suppose I accidentally load http://evil.com/, which sends a request for http://mail.google.com/. If the SOP were not in place, and I was signed into Gmail, the script at evil.com could see my inbox. If the site at evil.com wants to load mail.google.com without my cookies, it can just use a proxy server; the public contents of mail.google.com are not a secret (but the contents of mail.google.com when accessed with my cookies are a secret).
(Note that I've said "credential-restricted content", but it can also be topology-restricted content when a website is only visible to certain IP addresses.)
Sometimes, however, it's not evil.com trying to peek into your inbox. Sometimes, it's just a helpful website (say, http://goodsite.foo) trying to use a public API from another origin (say, http://api.example.com). The programmers who worked hard on api.example.com want all origins to access their site's contents freely. In that case, the API server at api.example.com can use CORS headers to allow goodsite.foo (or any other requesting origin) to access its API responses.
So, in sum, we assume by default that cross-origin access is a bad thing (think of someone trying to read your inbox), but there are cases where it's a good thing (think of a website trying to access a public API). CORS allows the good case to happen when the requested site wants it to happen.
There are security and privacy reasons for not allowing requests from anywhere. If you visited my website, you wouldn't want my code to make requests to Facebook, reddit, your bank, eBay, etc. from your browser using your cookies, right? My site would then be able to make posts, read information, place orders, etc. on your behalf. Or on my behalf with your accounts.
I currently have my server file set up like so:
export default createServer = (container) => {
const env = process.env.NODE_ENV
const allowedOrigins = process.env.ALLOWED_ORIGINS || ''
const allowedOriginsArray = allowedOrigins.split(",").map(item => item.trim());
const cors = Cors({
origins: allowedOriginsArray,
allowedHeaders: [
'access-control-allow-origin',
'authorization',
'Pragma',
'contact',
],
exposeHeaders: []
})
}
Here, I have origins set to an array of strings from my env file. (I have checked the cors documentation page, I believe there may be a typo, origins should be origin. Either way it does not seem to make a difference).
In my postman request, just to test it out, I have set the origins header to "http://www.test.com" (this is not one of the trusted origins i have in my env file). The requests succeeds when it should fail. I'm wondering if I am testing this incorrectly in postman or if something in my code is incorrect.
The Same Origin Policy is enforced by browsers to stop Mallory's Evil Website from sending your browser some JavaScript that will make an Ajax request to your online banking and send your credit history to Mallory.
CORS is used to relax the Same Origin Policy (so that websites can give access to the data you share with them to certain trusted other websites).
Postman is not a browser. You do not use it to visit websites. It doesn't execute JS embedded in those websites. Mallory can't tell it to make HTTP requests. Only you can do that. Postman doesn't need to enforce the Same Origin Policy, so it doesn't.
You can use Postman to make an HTTP request with an Origin header. Your server can then send back an appropriate Access-Control-Allow-Origin header. However, Postman won't do anything with it except display it in the list of response headers.
It certainly won't relax the Same Origin Policy because it doesn't enforce it in the first place.
From your comment on another answer, I think you have it backwards in your mind; Postman can add Origin because Postman is a tool for "faking" http requests sent by browsers, and to not have the ability to add an Origin would make Postman a poor faker. Origin might be used by some servers and Postman having the ability to send an Origin header means that whatever the server does with it, can be tested.
By and large Origin as a browser-to-server communication is little to do with CORS and SOP, which are a security that works in the direction of "server-to-honest-browser". Inclusion of an Origin header on a request may serve to cause a server to respond with CORS headers but you should not hard-link the two concepts because Origin can be sent for non CORS requests too, and CORS wouldn't specifically require any information in an Origin header in order to work
For CORS enabled scenarios the server says (in a response header) which sites are supposed to be using it and an honest browser (like most people have, when they install latest Chrome) decides whether the page on show should be fetching data from the server or not. Imagine a browser is showing delta.com and a script on the page tries to fetch some data from some back end server. Before it actions the request for the data the script wants the browser makes its own request to the server to check if it's OK; this is an OPTIONS request. If the server responds to the OPTIONS saying "only scripts served from acme.com should use me" and the browser isn't showing acme.com, then the browser doesn't perform the request the script on the page wants to do; instead an error appears in the console, because the delta.com script wanted data and the browser decided not to carry out the request after doing a quick "whisper in the server's ear"
A malicious actor using a browser that doesn't care about CORS requests of "please only use me if you're showing a page from acme.com" will be able to use the server anyway. Postman is an example of such a "browser" - it makes requests at your behest and doesn't care about CORS at all. If you were at all interested in making Postman behave like a browser, you should:
Pretend to be delta.com
Use postman to issue an OPTIONS to server.com (with an Origin)
Postman gets the "i only talk to acme.com" response in the A-C-A-O header
Your own brain decides not to issue the POST request you originally wanted to do, because you've realized you're pretending to be delta.com and the server only talks to acme.com
This would be a more accurate "postman pretends to be like a normal every day browser" iteraction
Origin can feed into CORS; the server could have 1000 different sites it is willing to allow requests from. Clearly sending 1000 site names in every response ("please only use me if you're showing a page from acme1.com, or acme2.com or... acme999.com") would make the response headers huge and the browser then has a thousand sites to check through. If the server is intelligent it can use the Origin to drive the response; if the submitted "Origin: acme547.com" is in the allowed list of 1000 sites the server knows of, then the server can send just that one acme547.com back in the "please only use me if.." header - it doesn't need to send the other 999 sites too. If the Origin is from delta.com, then it doesn't send a "please only use me.." header at all, causing an honest browser to fail the request
The requests succeeds when it should fail.
That's a misunderstanding of how the Same Origin Policy and CORS work. The request shouldn't fail, it's just that the response will or won't include information that a browser can use to determine whether to grant access to the information in it to the origin that requested the information. If the response doesn't allow the origin that requested it, the browser prevents code from the origin seeing the response.
Postman, not being a browser, doesn't do that (not least because there's no origin for it to test the response against).
You can't enforce this at the server side reliably, because malicious actors would just send your server what they think your server wants. The point is for the server to reply with information telling a browser who can access that information.
The goal isn't to keep the information the server sends back private (that's what SSL and authentication are for). It's to prevent a user's stored authentication information or active session from being used to steal information using taht user's credentials. Here's how it works:
Let's say Alice goes online to Bank B to pay a bill, and while she has that session active she also goes to check on what something costs and visits Malicious Site X to find out. Malicious Site X, hoping that Alice just happens to be a customer of Bank B, sends a request to Bank B's website for (say) Alice's account information. Let's say that request is a perfect mimic of a real request, so Bank B's website happily returns the information — because Alice does happen to be signed in with Bank B's site. Oh no!
This is where the SOP comes in. The browser knows that it was code on X's page that requested the information, and knows that it asked for information from Bank B's website. So it checks the response to see if the response specifically says "yes, it's okay to share this with X." Since the response doesn't say that, the browser prevents Malicious Site X from seeing the response and their evil plot to steal Alice's information is foiled.
From what I understand about CORS, this is how it works: I have a site foo.com which serves a page X. X wants to post data to another domain bar.com. If bar.com is CORS enabled (its headers produce Access-Control-Allow-Origin foo.com) then page X can now send data to bar.com.
As I understand to get CORS to work it's all about settingit up on bar.com, and has nothing to do with foo.com. It's all about making sure bar.com doesn't accept requests from any old domain.
However this really doesn't make sense to me. I thought CORS was designed to enable foo.com to dictate who X is allowed to communicate with. If we go back to the previous example but this time X is compromised by dodgy script so that it sends data secretly to evil.com, how is CORS going to stop that? evil.com is CORS enabled, and set to *, so it will accept requests from anything. That way a user thinking they using a site foo.com, are unwittingly sending data to evil.com.
If it is really all about bar.com protecting itself, then why does it make the browser enforce the policy?. The only conceivable situation in which this makes sense if you have evil.com serving up page Y that impersonates foo.com, that tries to send data to bar.com. But CORS is enforced by the browser, all you'd have to do is make evil.com a proxy that sends faked origin requests to bar.com (data goes from Y to evil.com, evil.com sets its fake origin to foo.com then sends it to bar.com).
It only makes sense to me if it works the other way round. foo.com is CORS enabled, and its headers are set to Access-Control-Allow-Origin bar.com. That way rouge scripts would get denied access evil.com by the browser. It then makes sense for the browser to enforce the policy because its running the scripts that could go rouge. It won't stop rouge sites from trying to send rouge data to bar.com, but bar.com can protect itself with a username/password. If foo.com has endpoints that it's expecting data back from X, then you can embed tokens into X, to ensure evil.com doesn't send data to it instead.
I feel like I'm not understanding something fundamentally important here. Would really appreciate the help.
However this really doesn't make sense to me. I thought CORS was designed to enable foo.com to dictate who X is allowed to communicate with.
No, it's about bar.com controlling use of its content.
But CORS is enforced by the browser, all you'd have to do is make evil.com a proxy that sends faked origin requests to bar.com...
Yup. And if you do, and the people at bar.com notice and care, they disallow requests from your server. You move it, they disallow the new one. Whack-a-mole time. But painful as that game of whack-a-mole is, it's a lot less painful than if the requests come directly from each individual user of foo.com, from their desktop.
Having foo.com enforce what foo.com can do doesn't make any sense. foo.com already enforces what foo.com can do, because it's foo.com that serves foo.com's content and scripts.
It isn't about Foo.com, nor about Bar.com. It is about user.
There are two things that CORS protects against. The first is access to resources behind the firewall. The second are resources that are normally protected, unless a request is sent from a browsers with authentication or other sensitive data cookies.
CORS is a Browser technology, with support from servers, that allows foo limited freedom to call outside of its domain. It is a restricted hole punched in the restriction against cross domain scripting.
Anyone can fake the ORIGIN header and create a CORS preflight or simple request -- Of course, anyone can directly connect to the Bar server directly and make the requests without using CORS at all. Any browser can directly connect to bar.com and get data. But a modern browser will not run a script from foo.com that access a bar.com resource. People visiting websites are protected against visiting a site designed to exploit cookies or the fact that the browser is behind the corporate firewall.
So the accepted answer is WRONG. It isn't about bar.com protecting its resources -- it does this through authentication and authorization. You don't have to create a proxy to send CORS requests -- you create a proxy to strip out the CORS requests (automatically responding to the preflight request, and returning the proper headers to the browser, but sending a normal request to bar.com). But you will still need authentication to get bar.com's resources, and foo.com would still need to somehow get you to install a proxy to exploit the cross domain scripting hole that CORS protects against.
But the concluding sentence is correct -- foo.com isn't in control of the resources -- it is the browser, with a quick check with bar.com to ask it if this is something that was intended.
From the OP:
If it is really all about bar.com protecting itself, then why does it
make the browser enforce the policy?. The only conceivable situation
in which this makes sense if you have evil.com serving up page Y that
impersonates foo.com, that tries to send data to bar.com. But CORS is
enforced by the browser, all you'd have to do is make evil.com a proxy
that sends faked origin requests to bar.com (data goes from Y to
evil.com, evil.com sets its fake origin to foo.com then sends it to
bar.com).
evil.com can already contact bar.com -- just like any human using a browser can (or curl or wget, etc). The issue is can evil.com force your browser to connect to bar.com, which may have IP filters, cookies, firewalls, etc protecting it, but javascript can connect to using your browser. So the Browser is the thing that protects the user. By disallowing cross domain scripting. But sometimes it is useful (ex: google apis, or a bank connecting to a bill paying service, etc) to cross domain script. CORS tells the browser that it is OK in this instance.
That isn't to say that there are no holes, or the the standard is the best, or that there aren't holes in implementation in the browser, or that sites are too permissive. But those are different questions...
I just found out that in order to allow cross-domain AJAX calls, Access-Control-Allow-Origin header should be set on SERVER side. This looks frustrating to me, let me explain why:
1) Typical use case is that the client wants to make a cross-domain request. I have never heard of a server trying to restrict access from alien webpages. Oh, I remember 'prevent images hotlinking', a funny feature of my hosting, which can be easily beaten by sending fake 'Referrer` header.
2) Even if server wanted to restrict connections from other domains, it's impossible to do this using capabilities of HTTP protocol. I suggest using tokens for that.
3) What's the use of blocking XMLHttpRequests while you still can use jsonp?
Can you explain why is this done that way?
For those who are still reading, there's a bonus question:
4) Do you know a way to prevent any cross-domain request from a webpage? Imagine a junior web developer creating a login form on a page having ads or other scripts potentially sniffing passwords? Isn't this the essence of web security? Why anyone is talking about that?
I have never heard of a server trying to restrict access from alien webpages.
The Same Origin Policy is a restriction imposed by browsers, not servers.
CORS is the server telling the browser that it can relax its normal security because the data doesn't need that level of protection.
Even if server wanted to restrict connections from other domains, it's impossible to do this using capabilities of HTTP protocol.
Which is why HTTP the protocol isn't used for that.
I suggest using tokens for that.
Using a nonce to protect against CSRF solves a different problem.
It's a relatively expensive solution that you only need to get out when it is the side effects of the request that can be problematic (e.g. "Post a new comment") rather then the data being passed back to JavaScript running on another site.
You couldn't use them instead of the Same Origin Policy to protect against reading data across origins because (without the Same Origin Policy) the attacking site would be able to read the token.
What's the use of blocking XMLHttpRequests while you still can use jsonp?
You can't use JSONP unless the server provides the data in JSONP.
Providing the data in JSONP and using CORS to grant permission to access resources are two different ways that the server can allow the browser to access data that is normally protected by the Same Origin Policy.
JSONP was a hack. CORS came later and is more flexible (since it can allow access to any kind of data, respond to request methods other than GET, and allow custom HTTP headers to be added).
Can you explain why is this done that way?
The default policy is "No Access" since there is no way for the browser to know if the data being requested is public or not.
Consider this situation:
Alice has an account on Bob's website. That account is password protected and has information that should be kept secret between Alice and Bob (bank records or exam results, for example).
Mallory has another website. It uses Ajax to try to access Bob's site.
Without the Same Origin Policy, Alice might (while logged in to Bob's site) visit Mallory's website. Without Alice's knowledge or permission, Mallory's website sends JavaScript to Alice's browser that uses Ajax to fetch data from Bob's site. Since it is coming from Alice's browser, all of Alice's private information is given to the JavaScript. The JavaScript then sends it to Mallory.
This is clearly not a good thing.
The Same Origin Policy prevents that.
If Bob, as the person running the site, decides that the information is not secret and can be shared publicly, then he can use CORS or JSONP to provide access to it to JavaScript running on other sites.
Do you know a way to prevent any cross-domain request from a webpage.
No. The webpage is a single entity. Trying to police parts of it from other parts is a fool's errand.
Imagine a junior web developer creating a login form on a page having ads or other scripts potentially sniffing passwords? Isn't this the essence of web security? Why anyone is talking about that?
"Be careful about trusting third party scripts" is something that doesn't get mentioned as much as it should be. Thankfully, most ad providers and CDN hosted libraries are supplied by reasonably trustworthy people.
Do you know an easy way of overcoming the problem of missing Access-Control-Allow-Origin
Configure the server so it isn't missing.
Use JSONP instead
Use a proxy that isn't blocked by the same origin policy to fetch the data instead (you won't get any credentials the browser might send because Alice has an account with Bob though).
I'm wondering if anyone knows a javascript library where I could remotely login to a site, then browse pages upon logging in.
This is very easy with php's curl but I'm wondering if there is a javascript equivalent where I can execute multiple remote url's under a single http session.
Basically what I'm looking to do is post a username/password to one of my sites and then immediately post some other commands to a different url (same remote domain) using the authenticated session.
I haven't come across anything like this yet so I'm wondering if anyone can point me in the direction (if it's even possible). Can definitely be HTML5.
Due to same origin policy restrictions in browsers this is not possible using javascript. You need a server side script that will act as a bridge between your site and the remote sites. Then talk to this local script using AJAX.
There are some techniques available to bypass the same origin policy:
If you don't need to read the response of your POST calls, you can create a FORM by javascript with an action to any url (not limited to the same origin policy) like in this question: How do I send a cross-domain POST request via JavaScript?
But this means you rely only on session cookies for the security, this is open for XSS attacks.
As you own the other domain site, you could develop a small service that returns a JSON with the data you need, and use the JSONP technique, eg:
<script src="http://otherdomain/curl?url=page.html&callback=cb">
</script>
May be you could signin before using the POST technique above and sending a secret token that you reuse in the url to improve the security.
And finally there is a way to act/read on other pages using a bookmarklet.The idea is to inject in the other domain's page a script that can run with all the privileges, and send back information to your domain.
But this requires a manual action (click a link or a browser bookmark)