Developers don't understand CORS
fosterelli.co
fosterelli.co
> So what would a secure implementation of this feature look like? The webserver listening in on "localhost:19421" should implement a REST API and set a "Access-Control-Allow-Origin" header with the value "https://zoom.us". This will ensure that only Javascript running on the zoom.us domain can talk to the localhost webserver.
"Access-Control-Allow-Origin" doesn't block the request from going through, it just prevents wrong-origin Javascript from accessing the response.
The original vulnerability actually just a slight variant of cross-site request forgery (CSRF) -- "wrong site request forgery" :-)
For the localhost server to detect wrong-site requests, there are two simple options:
1. Use a CSRF token. This requires a shared secret between the localhost server and the Zoom website.
2. Check the "Origin" header. This requires that the request be an HTTP POST, which is what they should be.
(Note: If you're determined to use some CORS machinery, you could build something more complicated based on pre-flight requests, but there's no point.)
On a GET request (like the one generated by a IMG tag) and all the other cases that do not require a pre-flight request ( https://www.w3.org/TR/cors/#preflight-request ) this is correct.
Are we clear on the thread model and what Zoom is trying to do? As far as I know, Zoom wants to make "joining a meeting" as easy as clicking a link. I do not think Zoom wants people to be logged in a zoom account in order to join.
And what Zoom should try to avoid is to have random people joining meetings and website forcing actions on the laptop of the people.
A simple fix is to use a GUID or any other long not-guessable string for the meeting id. And, of course, do not expose any other potentially dangerous endpoints beside the one that starts the meeting.
In general it should not be about either CORS or CSRF. There are better ways to start a video call communicating with a plugin than exposing a port on localhost and using <img src="localhost:12345/a-command?aParameter=12345" /> to communicate with it.
"www.visited-site.com" wants to start a meeting with you. Do you want to join?
is (uncommon?) common sense that the Zoom app should follow.
1. Only if the top level url bar says zoom.us is it allowed to cause any noticeable change to the zoom native app. This means some arbitrary website cannot cause any noticeable change to the zoom native app without redirecting you to zoom.us . This also means that a sandboxed iframe, such as an embedded web ad, has no possible way to cause a noticeable change to the zoom native app, because it is not able to do top level navigation to zoom.us .
2. When the zoom native app is triggered, the user must click yes on a confirmation button before joining the meeting.
I don't see people advocating that you need to be logged in to zoom.us .
Achieving requirement 1 without requirement 2 is still bad, because some website could redirect to zoom.us and force you into a meeting.
Achieving requirement 2 without requirement 1 is not too bad, but is annoying, because some sandboxed ad in a website could cause the zoom native app to pop up, annoying you, and could maybe almost be a DOS.
https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#Simpl...
If the server only reacts to POST, then Access-Control-Allow-Origin may be enough, because the browser will fist do a preflight OPTIONS, and if Access-Control-Allow-Origin is not set, the POST will not be made by the browser.
The security of the API can’t rely on strict validation of the content-type, how is that code supposed to make any sense to the 3rd generation of developers who are tasked to maintain it 5 years from now?
CORS is clearly not designed as a means of access control to ensure the POST request will never be made. Therefore, it is fairly catastrophic that an article prognosticating about how nobody bloody well understands CORS has also gotten it terribly wrong.
What this tells me is that there is probably big money in bug bounties for finding sites which use CORS for access control, and almost certainly are not checking the content-type.
Then again, only with TLS 1.3 do we get rid of RC4!! Except when downgrading to 1.2, 1.1, 1.0, ssl3 (is that even around?)
If you are willing to do SSLv3 the POODLE attack downgrades you and steals one byte of encrypted data per 256 iterations.
If you demand SCSV to defend against this downgrade, every implementation that speaks SCSV also offers a better protocol version than SSLv3 so you won't end up talking SSLv3 anyway, thus you should just not implement SSLv3.
You also shouldn't implement RC4 in 2019. Refuse to connect to peers that only offer RC4 instead.
You're assuming the API will remain identical, just with new headers. I didn't advise this, what they have now is not a semantically RESTful API.
What they have now is a mess made to fit within their image-hack's constraints, there's no sensible reason to keep the same GET pattern without that.
(Edit: It’s true that a proper RESTful API shouldn’t be using GET for operations with side effects anyway, but that’s different from knowing that avoiding it is mandatory for security.)
Just look at all the security options a dev has to contend with:
HSTS
CORS (with it's myriad of headers.. ACAO, ACAH, ACAC, etc.)
CSRF
XSS
CSP
SRI
.. That doesn't even count the other fun stuff like SQLi, XXE, LFI, RFI, SSRF, and on and on. It's become real obvious to me, that if the framework or language they develop with doesn't provide it enabled by default AND the most commonly searchable/referenced docs don't explictly tell you to disable it - it's likely not going to happen.I just went through this when setting up an nginx reverse proxy to a gunicorn web server. Once I was able to see all the X- headers and how wsgi was setting up its environment against that, it all became very clear to me what was happening and why each piece was necessary.
I think the same would apply to being able to see exactly what happens with preflight requests. PS: non-interactive diagrams don't fill that gap.
Here's what's wrong with your recommendation:
1. An AJAX REST API request doesn't necessarily mean "application/json". It could also be "application/x-www-form-urlencoded", which won't necessarily trigger a pre-flight request.
2. Your advice goes into detail on an aspect that is not the crux of this particular vulnerability ("Access-Control-Allow-Origin") but not on the aspects that are:
2a. How to handle the pre-flight request, which is the thing that would actually block wrong-site requests.
2b. If you're relying on a Content-Type of "application/json" to trigger a pre-flight request, then it should be made clear that this is now a security check -- it's common for server code to ignore the Content-Type header as long as the body is valid.
Oh the irony.
This applies to any advice that is not an actual reference implementation in code. There's only a state-changing GET handler because they chose to work around CORS and you can't POST for images. Implemented properly this CORS approach does protect against CSRF without the need for an explicit token. I've written more about this before, with concrete examples in Node for example: https://fosterelli.co/dangerous-use-of-express-body-parser
I'm not sure of that. There's tons of stuff in existence that has state changing GET handlers that don't have the goal of working around CORS, check out the HN upvote button. It's conceivable to me that if Zoom managed to send a CORS response header, they might have used an XHR with GET (considering GET is the default for XHR). But a discussion of GET vs POST seems largely irrelevant, because POST doesn't solve the problem.
>Implemented properly this CORS approach does protect against CSRF without the need for an explicit token.
The problem is that your definition of "properly" is "must reject requests with a Content-Type other than application/json". That's a non-obvious definition of properly that's not stated in the CORS article. That body parser article is better (although it does seem mistaken about XHRs being unable to make cross-origin requests). The CORS article would be better if it mentioned the Content-Type requirement and linked to the body parser article.
Although implementing CSRF protection via Content-Type is somewhat fragile. Someone might want to support a new Content-Type in the future and not realize that the Content-Type restriction is security-critical. Implementing it via an Origin check (or an X-Not-Simple-CORS: true header) would be clearer.
;-)
> The webserver listening in on "localhost:19421" should implement a REST API. This will ensure that only Javascript running on the zoom.us domain can talk to the localhost webserver.
Do you see how that is wrong?
Then you should edit your article to say that, or at least put a caveat saying it's more complicated than you imply in your article and to go look up the details elsewhere.
If I wanted to design an API that was prone to security issues - I would design something like CORS. While I understand why some requests require pre-flights and others don't, looking at it without considering web history: it is absolutely insane that changing what appears to be a non-security related parameter (Content-Type), drastically changes how the request is executed. Making it worse, is that its super easy to ignore the Content-Type header on the server side and just assume a that the body will be whats expected. Making it even worse, is that if a request is not pre-flighted, it will be sent to the server to be executed and only the reading of the response will be blocked by a non-matching Access-Control-Allow-Origin header - which is not intuitive at all.
What do you mean encrypt calls to localhost? Encrypt with https? That sounds hard since you can't get a real CA to issue you such a cert. I don't see much point in encrypting with https because people off your machine can't sniff or intercept your connection to localhost. Is your goal to protect yourself from other programs or users on your same machine? That might be a valid concern on a machine with multiple users, but definitely makes the problem harder.
Or do you want some custom application-level encryption? I can't think of a good way to implement that either, but maybe it's possible.
But since I don't know what attacks you're trying to protect against, I can't tell whether there's any benefit at all to encryption.
The SameSite=Strict is also an honorable mention as an up and coming technique once all browsers support it.
I’m personally at the point where I’d rather handle unnecessary request authentication than trying to do anything with CORS.
To truly understand CORS, you have to fundamentally understand 'the origin' as a security and execution context. Sharing means that content normally intended for another origin is shared with the initial origin.
Perhaps that is blatantly obvious and you are more confused by the myriad of headers, exceptions and lifetime. In that case, I propose to read more of the spec.
Ingest more teflon when in doubt about its toxicity.
(Above 300°C Teflon will generate toxic fumes, but then again, so will wood.)
Unless you are a parrot. I've heard it is disproportionately toxic to birds. (or is it just parrots?)
And why would I coat surgical instruments?
Edit: Just one article how healthy Teflon is:
https://fortune.com/longform/teflon-pollution-north-carolina...
Anyways I'm almost always using plain old steel pans and pots. There are very few things that stick so badly I resort to the one Teflon pan I have.
[1] https://theintercept.com/2018/07/31/3m-pfas-minnesota-pfoa-p...
Very little objective thought involved.
It allows you to make arbitrary HTTP requests across origins, even those that would normally be blocked by the same-origin policy, as long as the server collaborates and allows it, easily and in a secure way.
If you control the server (or can get someone to adjust it, or it already supports it), you can use it to make requests from one website to another, directly from the client.
For example, you can have JS on a site served on www.example.com make calls directly to api.someservice.net, without any kludges.
Any time you think of some contrived proxy solution, JSONP, or other kludges to bypass the same-origin policy, you want CORS.
For non-anonymous APIs, authentication can still be a challenge.
The important part is that CORS can be used to relax any existing same-origin restrictions on HTTP requests.
1. If the same-origin policy isn't a problem for you, no need to change anything, obviously.
2. If you want to _tighten_ the same-origin policy, CORS isn't going to help you (as far as I know).
3. You can get rid of any request-related same-origin policy restriction with CORS, easily and in a way that is easy to secure. There is no need to build around it.
4. If you try to use something custom to achieve that instead, you're likely making a dumb mistake, because you're not only creating unnecessary work, but also most likely either breaking many more users than there are users that don't support CORS, or introducing a gaping security hole, or both. Usually both.
- You visit evil.com.
- Evil tries to makes an HTTP request to bank.com/transfer.php
- But before the request goes through the Browser says, hey, wait a minute I first need to make sure if evil.com can access bank.com
- Browser asks bank.com, if evil.com (also known as origin) is allowed by sending an "OPTIONS" request first (this is before making the actual request)
- Bank.com replies in header saying "sure, everyone is allowed" (e.g. my bank), technically by setting a HTTP "Access-Control-Allow-Origin: * " header (or just evil.com instead of * )
- Browser is happy, it makes the requests and you get the data
Of course there is more to this like the server can tell which HTTP methods evil.com can do like "POST", "GET", "PUT", etc using "Access-Control-Allow-Methods". If the browser needs to send the cookies too with the request ("Access-Control-Allow-Credentials"), etc. But that is pretty much the gist of it as I understand.
Very naively, I don't understand why evil.com origin was allowed to make any request to other domains using any cookies/session/identity of the browser/user in the first place.
Why was this accepted standard? I was blown away when I first learned that a request made to xyz.com while in a browser tab showing abc.com would actually complete the request using my identity on xyz.com.
But there are valid situations where you want a request from 1 domain to be made to other domains. This is where CORS comes in.
CORS is a mechanism to loosen security, not increase it. It allows a server to say, these are the domains (outside my own domain) who can make requests. CORS headers should be set carefully so that you are only allowing the domains that should be allowed through.
Save a rather short-but-impactful list of exceptions.
> CORS is a mechanism to loosen security, not increase it.
Would that everyone shared your understanding.
Add in these two insights to those we are enlightening:
* CORS is enforced by the browser, so no, your curl command working doesn't say your service is fine
* That error message in the browser about 'no-cors'? It is 99% likely that no-cors is NOT what you want, so the error message is just misleading and unhelpful
...and you'll have covered my CORS wishlist :)
Or we could call it CORB instead (Cross origin request blocking), and then we see it's a mechanism to tighten security. Since fundamentally, what we have is an agreement against major web browser vendors that blocks cross origin requests unless the web server authors have used CORS.
I mean, how many people have encountered a problem with CORS? Almost no-one, and those that have encountered a problem with CORB and solved it by enabling a shitty CORS that opened the doors. (At least, they're fixing security holes in software that was written by devs who encountered a CORB problem and fuxed it. But all CORS problems follow a CORB problem.)
If we called it by its true name, maybe it would help people understand what's happening. Names are important. If developers understand CORB, they will potentially understand CORS. But no-one can understand CORS till they've understood CORB.
Cross origin requests are allowed (as long as they're simple). Reading the response is what's blocked.
Thus, cross-origin POST requests were allowed, and changing it was impossible without breaking the web. Likewise you can include images or iframes of other origins, allowing GET requests. However, you cannot read the responses! The JavaScript APIs just make it more convenient to make these requests, but they don't change what you can do - you still can't read the response. Not allowing it in the JS API would just make the attack more annoying (fall back to the legacy methods) and limit legit use cases.
It's important that you are not allowed to make arbitrary requests: As soon as you start using advanced features like custom headers (in other words, things you couldn't do with img tags, redirects, forms, and iframes), you run into preflight (where the browser asks the server whether the request is allowed before performing it, via OPTIONS, i.e. in a way that legacy servers will not understand and safely reject).
This is a compatibility feature that can never change, and shouldn't change, because we can't break the web. But my reading is that CORS is mostly a band-aid on what was a bad security policy to begin with.
The problem isn't that a browser might let a web page on a separate domain send and read any arbitrary request to another domain without permission -- with the exception of DDOS and bot attacks against servers, it's difficult to think of what risks this would actually pose to users themselves. And any native program on your computer can already send an arbitrary request to an arbitrary domain and read the data -- so there are at least few arguments that it might even be beneficial for websites to be able to do the same.
The security problem is that requests are made with your current cookies regardless of what domain you're on. Ideally, browsers from the start would have isolated every domain into their own container, so that if evil.com made a GET/POST request to mybank.com, it wouldn't be authenticated as me, regardless of how the server was configured.
We made it so that cookies could only be read by certain domains, but we didn't think until very recently to make it so that cookies could only be sent by certain domains. And at this point, too much infrastructure relies on this behavior for us to ever come up with a better model, so we're stuck with hacks like CSRF tokens which are dependent on websites being coded well.
That's not fully correct. Even without cookies it's bad if a.com can read a response from b.com . Why? Because b.com might be hosted on a private network, and contain private information. We don't want a.com to steal that information. Even if cookies aren't sent, there can be other types of ambient authority, and connectivity to a private network is one of those types of ambient authority. Another similar type is if a website is protected using a whitelist of user IPs. Side note: any website that uses this type of ambient authority that isn't associated with a hostname must also check the Host header to protect against DNS rebinding.
Of course if the web was built from the start such that a.com can read b.com just without cookies, then such websites that rely on non-host-associated ambient authority (hopefully) wouldn't be built and you would be right.
Just because a native program can do something doesn't mean a website should be able to. A native program can delete all my files without asking me. A website can't.
As an extreme example, if I were to stream video to your browser from my website, evil.com, I would have to either host or proxy those streams through my server. Instead, I can just point your browser to a video from youtube.com or something.
If youtube.com then decides you need to log-in (or be logged-in) to verify your age (as they do), then that is between your browser and youtube.com to negotiate -- evil.com never sees your credentials.
This is probably the reason that any url that makes changes to user data like logout.php, transfer.php, etc must be a POST request (with csrf token to protect again requests created via form submissons)
Also it's still possible to bypass CORS using JSONp requests or for submisson, but for that transfer.php must support jsonp too (by setting the callback method)
EDIT: Why isn't there a header that web servers can give out to browsers basically saying "Don't use the cookie/session I'm giving you ever, unless you're literally on this site"? I could see this being very useful for banks or other origins where they expect no requests except from direct users.
You can't make POST requests with ajax as CORS will protect you, but evil.com can create a fake form submission (with bank.com/transfer.php as the form's action) which can be a POST request. So in this case you match the csrf token which only you know and which is unique for the form/session.
It's a rabbithole :)
Try it yourself: https://news.ycombinator.com/item?id=20406633
There is, it's relatively new though:
https://blog.mozilla.org/security/2018/04/24/same-site-cooki...
- You visit evil.com
- Evil tries to make an HTTP request to bank.com/transfer.php
- The browser happily performs the request, authenticated with your cookies, and the bank, having a CSRF vulnerability, happily sends your money to the attacker.
- Since 'evil.com' and 'bank.com' are different origins, Browser refuses to provide the response to evil.com, but the attacker doesn't care, he got the money.
CORS allows you to relax these restrictions, not tighten them.
Now, bank doesn't like these attacks. So they make the legitimate application send an additional custom header, "X-Totally-Secure: true". Despite being a really bad idea, if (big if) the browser follows the standards, this prevents the attack:
- evil.com tries to make the HTTP request as before
- Browser lets it through, as before
- Bank rejects the request because it's missing the magic header
So the attacker adds the header to the request:
- evil.com tries to make a non-standard HTTP request to bank.com/transfer.php, with the header attached
- BECAUSE IT'S A NON-STANDARD REQUEST, browser asks bank.com (as you described, OPTIONS)
- Bank.com replies "wtf do you want I don't know what OPTIONS is"
- Browser refuses to make the request
Unfortunately, the bank forgot that they have a marketing department, that runs ournewbankapp.com, and shows your current balance in the fake screenshot of the app to show how awesome it is. And your bosses' bosses' boss has yelled at the IT department that rolled out the security measure to make it work again. They make ournewbankapp.com send the magic header (including access-control-allow-credentials), but now the OPTIONS request fails. So they teach the web server to respond with "everyone is allowed" (with "access-control-allow-origin: *" as you described) because they're lazy and dumb.
But because browser vendors know that developers are lazy and dumb, the browser completely ignores this: If access-control-allow-credentials is set, the allowed origin must be listed explicitly. The developers give in, and explicitly add ournewbankapp.com to the header, and now it works, but the attack doesn't work.
(part 2 follows)
Also glad I didn't mention jsonp. God knows what the devs at ournewbankingapp would do with that one ;)
- Evil tries to make an HTTP request to bank.com/transfer.php
If this is a regular HTTP POST (ie submitting a form, and the browser window changes location), the browser will allow it.
If this is an xhr POST, the browser, following the same-origin policy, allows the request, but prevents accessing the response (unless allowed with CORS).
[EDITED with tgsovlerkhgsel's help!]
document.getElementById("myForm").submit();It is possible that XHR, or common XHR libraries, default to adding some header that makes it a non-standard request, but a fetch() call works.
In Firefox, open a debug console and run
fetch('https://otherorigin.example.com', {method: 'POST', body: 'blah'})
You will see two things. In the console: Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at https://otherorigin.example.com/. (Reason: CORS request did not succeed).
In the network tab, a HTTP request.Replace with a URL of a server you control, or run `nc -lnvp 9999` and replace the URL with http://127.0.0.1:9999, to see that the request is indeed being made.
As the author said... few people understand CORS.
Nothing with a Cookie header for example...
r = new XMLHttpRequest();
r.withCredentials = true;
r.addEventListener('load', function(e) {console.log('loaded: ', this, e);});
r.addEventListener('error', function(e) {console.log('error: ', this, e);});
r.open('GET', 'https://www.google.com');
r.setRequestHeader('Cookie', 'somecookie=somecookievalue')
r.send();
But this will send a request with the automatically set cookies just fine: r = new XMLHttpRequest();
r.withCredentials = true;
r.addEventListener('load', function(e) {console.log('loaded: ', this, e);});
r.addEventListener('error', function(e) {console.log('error: ', this, e);});
r.open('GET', 'https://www.google.com');
r.send();
Assuming the user is already logged in to bank.com , the user's cookies will be automatically sent on the request, and the transfer will go through assuming there is no CSRF protection.- instead of hardcoding the allowed value, they set it to always echo the value of the Origin header - browsers are powerless against that much stupidity, and can't distinguish it from a correctly configured server that is allowing the request. evil.com sends the request, bank says "evil.com" is allowed, browser shrugs and sends the request.
- instead of using CORS, they could have tried to build their own custom solution that allows ournewbankapp.com. The attacker would have to analyze how it works, and would then most likely find a way to exploit it to perform the attack, while legitimate users with privacy extensions would make the support hotline despair due to countless people complaining "I can't send money".
- instead of adding the custom header, they could have decided to check if "origin" is present, and if so, assume it's a cross-site request and check the origin against a whitelist. This still relies on standard-compliant browsers but isn't the worst idea AS LONG AS YOU REQUIRE AN ORIGIN VALUE, AND TREAT LACK OF AN ORIGIN HEADER AS A FATAL ERROR, BEFORE TAKING ANY ACTION (e.g. the money transfer) BASED ON THE REQUEST. I think can force same-origin requests to include the origin header with some option on fetch(). But if you treat a missing (or "null") header as ok, the attacker will likely find a way to make some browsers send no header, and steal money again. Sending "null" (or some other default value, been a while since I played with it) is easy, I think with some iframe trickery.
- they could have used plain HTTP, exposing them to DNS rebinding attacks even from remote attackers who cannot sniff their user's traffic.
- they could have an XSS vulnerability somewhere on the main site or the whitelisted marketing site, allowing the attacker to proxy his malicious requests through that site (and thus make them use a whitelisted origin). Maybe some long-forgotten kludge designed to proxy requests to third parties that don't support CORS...
They set it to check a regex for `bank.com` to also allow `subdomain.bank.com`, but inadvertently also allow `bank.com.evil.com`, `evilbank.com`, or similar.
XSS is essentially completely separate from CORS and CSRF. XSS is a bad vulnerability, worse than CSRF, and you can't expect any type of CSRF prevention to protect against XSS.
I've been in this situation before with web-based cross-platform apps using Cordova. As far as I remember, I ended up echoing the origin domain if it matched a set of allowed domains (basically localhost and example.com), since regular expressions or lists are not supported in allowed-origin headers. For session tracking, I used tokens rather than cookies to avoid CSRF issues. Would there have been any better solution for this? I think not.
https://github.com/OWASP/CheatSheetSeries/blob/master/cheats... is probably one of the best documents discussing this, including the drawbacks of the header method that I used (and warned against) in my example:
https://github.com/OWASP/CheatSheetSeries/blob/master/cheats...
Am I missing something or is this transaction actually a browser being the deciding factor for whether or not the request gets sent?
If that’s true, couldn’t a nefarious browser decide when to push a request and completely ignore the OPTIONS header?
Both CORS and CSP are for guarding against malicious code on the web, being executed by non-malicious standards-following browsers used by non-malicious users. (the user is in fact who is being attacked).
I think is frequently confused, and it leads to a mistake in the other direction, people thinking CORS or CSP can guard against malicious user-agents operated by malicious users. It is not for that!
CORS prevents a malicious site from exfiltrating/accessing data using the access you have, for example an internal site that is on your computer's network but the malicious website's servers can't directly access.
However, this attack is most useful if you can get the victim's browser to send the request for you, because that way, you can get it to include the victim's authentication cookies.
If you as the attacker send the request yourself, you don't have the cookies.
If you make the victim's browser send it, you either can't make them use a nefarious browser, or you already won since you have code execution on the victim's machine.
No web standard can protect you from a nefarious browser, since the browser could just decide to not follow the standard.
But stick with it. It does slowly sink in.
Something that especially confused me at first was that all of the CORS blocking happens in the browser. So you never run into CORS using curl, etc.
Among all the W3 specs, the CORS one is definitely not the largest spec. See https://www.w3.org/TR/cors/
Ultimately the Zoom vulnerability reminded us that there is always a trade off between usability and security. Most of the developers default to usability and simplicity.
The average developer looks for the fastest way to achieve "what I want to do". And they do not even question installing stuff with "curl https://any.domain.com/any-script | sudo bash".
And that is the root of all evils.
For example, I've gotten decent at regular expressions a few times but have to keep relearning it. Adding insult to injury, I might relearn something from Stackoverflow, and realize afterwards I posted the answer!
Not for trivial stuff. But a string of punctuation with capture groups or whatever, forget about it [1].
Any support for this informal draft definition below?
------------------
Teflon tech (draft definition v0.1 [2])
Knowledge that could be learned by most with reasonable effort, but often must be relearned or is never fully grokked.
This can be due to infrequent opportunities to recall the concepts in a particular role, or insufficient need to apply (thereby practicing) the relevant techniques. It may also occur when a tech is used frequently but the foundational theory is hidden by the use of higher level abstractions.
[1] https://stackoverflow.com/questions/1381097/how-do-i-get-the...
[2] content provided under MIT license.
Probably my having a super low opinion of it's whole ecosystem doesn't help. ;)
The Javascript ecosystem is huge. It's also the wild west, completely non-curated, open to anyone, and you have to treat it accordingly. It's not really one entity.
But... sigh I feel exactly how you feel about Git about Vim. It's the one editor everyone has decided despite it's arcane and bizarre UX that you aren't a real programmer if you don't know.
Refuse to learn it. Absolutely refuse.
I think everyone has one of those things.
In the 2000s I learned Perl no less than three times. I was really impressed by what people could do with small scripts and one-liners, and I wanted that power for myself. I got really good three times, and each time it took me less time to forget it than it took me to learn it. I did not agree with its decision in that case, but I lost the argument.
I shall graciously accept this being called adtac's law out of sheer humility :P
I'm ok with regex too, but if you asked me to do something with look-behind I'd have to experiment a little to figure out how it works again.
Also, the author makes a good point that `Access-Control-Allow-Origin: *` is pretty dangerous. I hadn't really worried about it in the past, because "I don't care who calls my api, they aren't authenticated". But if malicious client code got a hold of a user's session (using XSS or what have you), I'd be open to them steering my user's session and doing horrible things. Definitely going to review my current projects with this in mind.
Edit: of course, if I have an XSS vulnerability, they'd be able to do that from my domain anyway, so CORS doesn't 100% fix the problem. XSS is bad.
What would you use instead of that? Suppose you're doing something like client-side Blazor that calls to a WebAPI project. How can you improve security by restricting the origin of access requests when your client is open to the public?
If by "open to the public" you mean "open to requests from any client domain", CORS isn't going to help. In that case I'd probably have api clients pre-register a whitelist of domains that they're planning to make client-side requests from, so you can check the domain against the whitelist and dyanamically build your allow-origin header.
Dynamic allow-origin sounds magical, but is really straightforward. You just look at the `Origin` header of a request (e.g., in express `req.headers['Origin']`), compare it against your database of whitelisted origins, and if it's in there, return it as the value of `Access-Control-Allow-Header`.
If you don't have any relationship with the folks using your frontend, I'd just "turn it off", that is, use "Access-Control-Allow-Origin: *". It's a security issue only insofar as you don't trust the third party that owns the web frontend to handle their users' data securely, either by introducing their own security vulnerabilities, or by hijacking users' sessions themselves. The big question I think is whether the third party's users are your users too, in which case you're responsible to vet the third party to protect your users. If you're just a backend for whatever-the-heck, just make sure you have a good terms of service for the api so you're not assuming responsibility for other people's mistakes/malice.
That said, yes, when you want to allow multiple origins, reflecting the Origin request header in the Allow-Origin response header is the only solution that works. (Note however, that sometimes the Origin header is not present, an additional difficulty.)
https://www.w3.org/TR/cors/#access-control-allow-origin-resp...
See the note.
CORS, when implemented correctly, ensures that a session is not hijacked by a malicious website's JavaScript in order to call your API in the context of the session (effectively masquerading as the user who authenticated with your API). This scenario assumes that there is an authentication session cookie, tied to your API domain, that the browser would pass along with any request to your API domain (of course there are SameSite cookie and third party cookie blockers that can mitigate these situations as well, but perhaps "trusted" cross domain requests are desired in this use case)
With CORS allowing traffic from anywhere on the web, you can't reliably trust that the authenticated sessions to your API are not being used in phishing / side channel attacks: I discover your are authenticated on site on foo.example.com and I send you a link to my website, evil.com. Evil.com includes JavaScript to request data from an API on foo.example.com. Your browser executes the JavaScript and makes the request and gets a response payload, and since I'm a jerk I then post that same payload to my own endpoint on evil.com to capture the data.
Of course this all assumes that you WANT your API to be accessible cross origin. If you provide an API as a product this is common, since it allows other developers to build web apps against your API. If that is not a use case, then same origin policy (and no CORS headers from the API server) is sufficient to prevent malicious domains from doing bad stuff with your users' authenticated sessions.
So, we provide the data API that these web widgets communicate with, and the web widgets themselves can be embedded on our customers' own websites (some-company.com). However, the customer can decide exactly which hostnames can embed the widget (through a control panel we provide), and these hostnames ultimately become the `Access-Control-Allow-Origin` value we provide with every API response. Perhaps they want it on foo.some-company.com or bar.some-company.com -- it's really up to our customer where they want these widgets to be embedded.
By doing this, the customer knows that no other website can host these widgets, thus exposing their consumers' data to a phishing attack as I outline here: https://news.ycombinator.com/item?id=20405169
But what is the alternative when you have a script that is going to be deployed on multiple sites that you do not control? Which origin do you specify? This is the scenario which always trips me up and results in kludgy workarounds.
It would be up to you that only the URL for such scripts (not your entire site) have `Access-Control-Allow-Origin: ` , and to make sure that there is nothing malicious JS can do with `Access-Control-Allow-Origin: *` at those particular URLs.
Which is confusing to figure out, it's true, because the whole thing is confusing, indeed.
I think that "something" is the fact that the client does the blocking. Generally the servers are perfectly willing to provide the data. Heck, if you sniff the packets, you could even look at the data blocked by CORS (assuming you're working around TLS). It's especially confusing at first that tools like curl work no problem, while browser block everything.
I think the point of this stuff is that, if the user's session is stored in a cookie, and malicious javascript can send HTTP requests to your site (that will use whatever cookie is already present) and read the responses -- they can steer your session without needing to get a hold of a user's session using XSS or anything else.
They already HAVE a hold of the user's session, because they can control the browser, which does.
The point of cross-origin restrictions is to prevent JS loaded from one site from steering the user's session on a different site. (Maybe among other sorts of attacks).
CORS lets you disable those protections. `Access-Control-Allow-Origin: ` disables them entirely.
I could have some of this wrong, I find this stuff confusing too.
But I believe they do not need to "get a hold of a user's session (using XSS or what have you)" in order to steer a user's session under `Access-Control-Allow-Origin: `
The important point though: You don't need any pre-existing vulnerability on your site in order for "Access-Control-Allow-Origin: *" to create a vulnerability.
This stuff is sure is confusing to talk/think about though.
WIth images, fonts, or other static content the risk is arguably fairly limited except for the fact that browsers tend to have all sorts of nasty bugs with malicious content overflowing buffers thus leading to arbitrary code execution.
But of course the elephant in the room is that doing things with headers adds a lot of friction for developers. Now their simple application has a devops component. E.g. modifying nginx to add that header or trying to make AWS CloudFront forward CORS headers is just a royal pain in the ass. It's also very fragile because these things break easily and are rarely covered by integration tests because these things tend to be specific to infrastructure and environment.
> Follow up article: that thing about CORS is not right
> Hacker news top level comments: that article's not quite right
> First child comments: tlc is also not exactly right
I feel like I understand CORS even less now. Article title is right, I don't understand CORS.
Once you grasp both of these things, then you have the base for how and why CORS exists. Until then it's mostly an annoyance.
Ask yourself why many developers need this path. The reason is that creating plugins for various web-browsers is a very expensive ordeal, with Firefox and Chrome having a completely different ecosystem. Moreover, in my opinion the plugin ecosystem is less secure than having good engineers take the localhost path (with challenge-response token authentication and origin hostname validation, obviously).
If for some reason that doesn't work for your app, the post also mention two secure alternatives: the native client can install a self-signed cert, or you can use a browser extension with the native messaging API. You touched on this too!
My point here is not the specific example solution -- there's lots of alternatives and yes it depends on the situation and browser support requirements. But NONE of them are a reason to allow requests from every origin.
[0]: https://bugzilla.mozilla.org/show_bug.cgi?id=903966
[1]: https://chromium.googlesource.com/chromium/src.git/+/130ee68...
[2]: https://github.com/w3c/webappsec-mixed-content/commit/349501...
2016 is in the 50s, and this[1] claims that version <67 has single-digit usage share.
I think this is a myth, especially on desktop/laptop PCs - a few hundred bytes of entropy at system boot initialize the kernel CSPRNG, which can then generate countless gigabytes of cryptographic-quality randomness on demand.
I thought about this problem and there are two workarounds. First workaround is to get agreement with major CA who would allow you to issue a valid certificates for users. So it's like user installs your software, generates private key and you generate signed certificate for that key on your server. I think that plex does that, but it's probably extremely hard and fragile scheme. Second workaround would be to proxy traffic from your localhost server to your remote server. Remote server would present valid certificate for something like local.yourcompany.com and would decrypt traffic and translate it back to your localhost server. Same with response. So you're doing encryption with remote server and never leak your private key. I'm not sure if CA would be happy with that implementation, but technically I believe it's not a key compromise.
The second workaround I didn't think about. Do you mean we'd change the resolver to resolve 'local.yourcompany.com' to 127.0.0.1 on the local machine? That would work, but would introduce quite some extra latency and add some fragility.
That’s simply wrong. You have outdated information. Unless you don’t consider Firefox, Chrome, IE, and Edge to matter. https://chromium.googlesource.com/chromium/src.git/+/130ee68...
https://developer.microsoft.com/en-us/microsoft-edge/platfor...
Safari still has an open issue I think. But that’s no surprise since Apple has demonstrated they’re suckitude at software security and development in general.
Oddely, the issue was updated today after months of inactivity. Maybe they became aware of their part of responsibility in what happened to Zoom?
I'm not sure I do, I get confused, although I think I have in the past.
I think better docs are needed, I haven't found really good educational materials that begin with the big picture (threat model, what are we actually trying to do here, what things are we trying to guard against, what things are out of scope for CORS to try to guard against), which to me is the necessary foundation to get your head around it: Where you need it, and what you will want to make it do.
E.g. "lets restrict access-control to our API endpoints so it only responds to requests from our website". This is a valid use case, but it's meant to protect a web browser user. An evil website won't be able to request their profile data from our API, and CORS makes it possible to relax this protection to allow our other web property on a different domain access to this information. If someone wants to scrape the API, they can just use cURL and not care about CORS at all.
[0] https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Ac...
The basic security in we browsers is based on SoP: Separation of Origins. To simplify, there are number of actions that can be done within an origin (a domain, to simplify) that are prevented across origins (across domains, to simplify). CORS is way for bridge 2 different origins, i.e.e to allow some specific actions between 2 different origins.
One example: by default, XHR requests can only send a specific set of HTTP headers to a different origin. If b.com want to accept the header X-Special-Header from a.com, it has to whitelist it - Access-Control-Allow-Headers: X-Special-Header, Access-Control-Allow-Origin: a.com. On a.com, before the web browser makes an XHR request to b.com that includes the header X-Special-Header, it has to check whether b.com authorizes it or not. I sends an OPTIONS request to b.com and check for the HTTP response headers Access-Control-Allow-Origin and Access-Control-Allow-Headers.
Since CORS is a way to bypass the Separation of Origin basic security model, you should be careful with it. For example, you may allow any site to get read your content, i.e. trigger a CSRF request on behalf of the user logged in to bank.com, and with Javascript be able to read the page content, including the account number because bank.com set the CORS to authorize any remote site to do anything, basically putting bank.com in the same origin as any other domain.
Here's a piece of unsolicited advice: avoid CORS if you can. For user facing web apps, put your APIs behind a proxy. For a dev environment in which you want to mix local assets with production assets, use a proxy. In general, if you can't solve a cross origin problem with a proxy server, then you should really stop and reconsider what you're trying to do.
CORS is full of unpleasant subtleties, as many of the comments below illustrate. Different browsers implement CORS differently. Want to cache that preflight request? You'll use the header `Access-Control-Max-Age` for that. Except Chrome doesn't respect respect that header; the cache TTL is actually hardcoded to 10 minutes [2]. Except, according to this bug [3], Chrome will respect your cache headers starting in July.
[1] https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS
[2] https://stackoverflow.com/a/23549398
[3] https://bugs.chromium.org/p/chromium/issues/detail?id=131368
"For very intentional reasons, the browser explicitly ignores any CORS policy for servers running on localhost."
That last sentence is incorrect – Chrome does respect CORS headers for localhost webservers.
It's only partially incorrect. In most cases, CORS will not work with localhost, at least without a self-signed certificate trusted by the browser.
If for some reason that doesn't work for your app, the post also mention two secure alternatives: the native client can install a self-signed cert, or you can use a browser extension with the native messaging API.
[0]: https://bugzilla.mozilla.org/show_bug.cgi?id=903966
[1]: https://chromium.googlesource.com/chromium/src.git/+/130ee68...
[2]: https://github.com/w3c/webappsec-mixed-content/commit/349501...
In Zoom's case, I highly doubt CORS on its own was a viable solution. Maybe in 2026, a decade after the patch, sure, but in the current climate, it's not reasonable to expect that all users will be using browsers that have adopted these changes in 2019.
It is still blocked.
(Also you might want to verify you were using 127.0.0.1 and that you had the headers correct)
Installing a self signed certificate is easy on Edge and Chrome. But did you ever try to do it dor Firefox? Firefox has its own certificate store implementation and only native code to manipulate it. So it’s easier to provide a valid certificate instead even if it means that the private key is exposed.
Concerning the extensions, this was not practical until the recent unification under web extensions. I never tried.
I would think that they could distribute the cert (and the key) and have it work. [Edit] Unless browsers detect that it's a local IP address behind the domain name and still consider it a special case of origin.
https://blog.filippo.io/how-plex-is-doing-https-for-all-its-...
In the case of LetsEncrypt certificates, there is even an API for this revocation.
However, given how ineffective revocation is, it unfortunately could still be a viable strategy.
The easier approach is, of course, using the fact that browsers now consider http://127.0.0.1 (and/or http://localhost) a secure origin to avoid this issue.
You only need CORS if you need the browser to act as a middleman to pass information back. IE: Credit Card Payment IFRAME
If you screw up CORS implementation it just means that anyone can read any information set by your website.
https://www.moesif.com/blog/technical/cors/Authoritative-Gui...
The CORS standard needs to be radically redefined from the ground up with developer ease of use as part of the consideration.
Put together a little script package this week, threw in some Vue and a Bootstrap and put it on our fileserver.
Now the Font Awesome icons won't load due to CORS errors.
I don't want to spin up a server for 500 lines of HTML/JS. I don't want to download the fonts, fiddle around in Bootstrap etc.
CORS is broken or the web itself has become broken if we need integrity checks and cross origin protection for using fonts.
1. The website JS is served from a domain say "website.co" to the browser when you visit it.
2. If this JS tries to make a XHR to a domain that is NOT "website.co"(not the origin of JS) the browser first sends a preflight request (OPTIONS) asking for "guidance" from this second domain.
3. The Web Server on second domain responds with "a request" to block/allow XHR calls from JS served from certain domains.
4. The browser chooses (by default) to not make the GET/POST call if the JS domain(website.co/*) is not in "Access-Control-Allow-Origin" header.
There are other nuances but that is it really. Things to note
1. The browser enforces CORS. Not the web server. You can disable this enforcement with a flag in both Chrome and FireFox.
2. Since only browsers enforce CORS, other tools(cURL, PostMan) will successfully make GET POST request regardless CORS config on the webserver.
3. If you could intercept (using a proxy) and change headers in response to preflight request you can bypass CORS on browsers. 3.
https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#Simpl...
Oh wait, I just described CSRF!! Zoom, here is your egg. Now promptly smash it on your face; thank you.
1. Attacker installs zoom.
2. Attacker starts to join meeting foo.
3. zoom.us creates a signed request saying "join meeting foo" and gives it to Attaker.
4. Attacker takes that signed request and sends it from attacker.com to localhost inside Victim's browser.
5. Victim's zoom native app gets the request, validates the signature, and joins the meeting.
I think it can be modified to be safe if there's a key exchange between zoom.us and the native app, and zoom.us signs the key exchange with its private key. But this seems hugely overkill compared to a simple Origin check, or even compared to a traditional XSRF token (via a cookie on localhost).
I hit this trying to display map tiles. The map tiles are on a server that doesn't send any CORS headers. A simple 2D display of the map works fine. But using a 3D library that allows rapid movement, perspective views, and flyover hits a CORS block. I can force through that with a browser add-on, but that's just for testing.
Incidentally, CORS-plugin for Firefox has a major security hole. You give it a URL to allow, but, randomly, it switches invisibly to "all URLs" and opens a security hole.
CORB - cross origin request blocking - is what prevents you from doing it.
This sounds pedantic, but it seems that till people understand that browsers implement CORB unless a server implements CORS (or in whitelisted/legacy circumstances), they cannot understand their problems or the solutions. This is largely the fault of browser vendors, who seem to act as if everyone understand CORB and their only task is to educate you about CORS.
Here's a fact-based starting point to learn about CORS: https://en.wikipedia.org/wiki/Cross-origin_resource_sharing
Setting up some local shizzle on production server that is callable from frontend ... Just stop, you can have microservices that listen on local interface but please call it from behind api gateway. If you have some different domains you need to use then yeah make subdomains? I don't know just hire some Ops people and make them work together with Devs.
All I really learned is that you can't make calls to services on another domain unless that service allows it via CORS.
Eg. Every robot I want to websocket to has to be cool that the web application originally loaded off of (and in a way is owned by) the central server.
See https://w3c.github.io/webappsec-cors-for-developers/#cors
> Enter Cross-Origin Resource Sharing. The challenge when designing CORS was how to enable web applications to request and receive more cross-origin permissions, without exposing existing applications to new attacks. By the time CORS arrived on the scene there were already billions of HTTP resources in use by a wide array of applications beyond the traditional browser. Many of those resources relied implicitly on the original permission table. For example, they might expose a privileged endpoint on an intranet, but check for a special HTTP request header that only a non-browser or same-origin client could set, to protect themselves from CSRF-type attacks. The design of CORS couldn’t suddenly make all those existing applications vulnerable; it needed to evolve the web platform in a backwards-compatible way.
If we had started with the assumption that you can make requests and receive responses from any site to any other site, then we would not have the confusing mess that is CORS.
> The story behind * Why does the * mode of CORS behave so differently than the credentialed mode? Given the already long history of web vulnerabilities attributable to abuse of ambient authority, there were two schools of thought about how to expand the web platform with cross-origin requests. One camp proposed extending the already-familiar cookie model to authenticate to cross-origin resources. The other camp felt that ambient authority was a mistake in the architecture of the web, and advocated for cross-origin requests free of any ambient credentials or origin-based authentication. (Such requests, they also argued, could be exempt from the Same Origin Policy entirely, as they would be equivalent to loading from an unprivileged proxy.) The XDomainRequest object in Microsoft Internet Explorer 8 and 9 retained some of this style - it had no support for credentials, only anonymous requests, although it also used the same Access-Control-[...] headers.
> The familiarity and compatibility of the cookie-based credentials model of CORS eventually won more favor with developers than re-designing their applications in a capability-security style. (https://www.w3.org/TR/capability-urls/) As a compromise, the anonymous request mode was retained as a “subset” of CORS. Unfortunately, the subtle differences in architectural style that persisted and the choice of * to represent such requests has been a consistent source of confusion.
TL;DR Hosting cross domain web widgets or customer engagement experiences like chat windows, etc
https://lists.w3.org/Archives/Public/public-webapps/2009AprJ...
Very interesting to read this now.
The whole pro-CORS argument essentially boiled down to "it's easier to understand when you've already internalized access lists". Capability folks then replying, "sure, but it doesn't help you understand the proper authorization contexts, and so doesn't solve the confused deputies, and in fact, hides some of them to bite you later". Basically, the same old arguments. Rinse, repeat.
But the point is, it shouldn't be that complex and I shouldn't have to dive deep into the docs to make it work. That is as much an impediment to CORS as is the lack of understanding.
By the logic you're outlining there's no reason to sandbox a browser at all, since it's no different to an operating system. Experience suggests that would not be wise.
It is a protection of your site's users, using good non-malicious browsers, against malicious other JS on the web.
I think this is one of the most basic misunderstood things about CORS.
Once you understand that, you can start actually trying to understand the threat model... which is still pretty confusing, to me anyway. But until you are there, you haven't even started.
Seriously, if it gets to the point of having to write "{Class of professionals} don't understand {Obscure restriction that doesn't make much sense nowadays}" it's likely not a security issue anymore, it's a UX issue (with the developers being the users). At the very least prompt the user to (dis)allow CORS when a request is being made, similarly to how the user is warned when running an unsigned executable on macOS.
First of all, when you are using a web browser, if it wasn't for cross-browser request restrictions, code executing on one site (or web app) would be able to _use credentials stored in cookies_ by another site altogether. Because all these web apps exist in the same browser context. Native apps are all separate, code running in an Native App A can't say "make a request to facebook using the credentials the user already logged into in the facebook app." But Web App A could do exactly that with the credentials the user logged into on facebook.com -- if it wasn't for cross-browser request restrictions. Which then CORS let some sites carefully opt out of.
Secondly, when you download an app, you are trusting the developers of that app.
You navigate the web, you are trusting every single site you visit (to also not have their own code injection or other vulnerabilities to which web pages/apps are particularly vulnerable to), and most users have no idea what sites they are visiting, they are just clicking links.
What we really need a more clear explanation of the CORS threat model, with examples. I think we'd all be more clear about what it's for and why we need it if we understood it better.
It is definitely a developer UX issue, but it is sadly one that is baked into the web for legacy reasons.
In other words, it's to stop the developer of fakegoogle.com using your web browser to access google.com as if they are you.
Therefore, if you've written an app which will only ever contain cookies and authentications you've permitted, and only ever access servers that you've specified, then yeah, sure, CORB is irrelevant and you can safely ignore them. You and the malicious coder can both write code that runs on Android or iOS safe in the knowledge that there's an absolute sandbox between them - nothing the malicious coder ever does will ever leak your user's secrets to your server.
Likewise, if you're a malicious coder and you convince the user to give you their legit google.com secrets, you can safely send them wherever you want.
If you're writing a web browser equivalent app, that will run arbitrary code from untrusted third parties and store and release private or secret information, CORB makes sense and you should pay attention to the CORS headers.
CORB is solving a problem inherent in web browsers - they run untrusted code and code can cause your secrets to go to your server. It is annoying to you as a web app writer in the same way that locked doors are annoying to plumbers. It would be so much easier for the plumber if they could just come around to my house whenever it's convenient. And it doesn't solve any problem the plumber has (if some random steals all your gold, how is that the plumbers' problem?). Inside your house you don't lock every door. But you do lock the doors that separate trusted and untrusted people, and no amount of difficulty to plumbers will change that.
Consider this argument: CORS basically ensures that browser and servers cooperate to protect end users from a "smart" user agent that happily throws around cookies and/or auth tokens. Remove cookies and basic auth headers, what's the point of CORS?
Happy to hear thoughts from folks more knowledgeable in web security than me. If not, let's please get together to propose an RFC for the CORS-GTFO header (a server header indicating that a browser does not have to do wasteful preflights etc.)
The ability to communicate with a domain other than the one your app is running on. For instance, if my site on www.example.com wants to send a POST to www.example2.com, I need CORS. example2.com needs CORS to specify that only example.com is allowed to send a POST to it, not anyoldaddress.com. example2.com could look at the Origin header and refuse connections, but it would be vulnerable to DDOS attacks.
Now, if your SPA is entirely self-contained and self-hosted then no, you don't need CORS. But there are plenty of situations where that isn't the case.
The crypto security space has the same issues with hard-to-implement standards and impossible-to-configure libraries -- the likelihood of misuse/overly permissive settings in frustration is really dangerous.
I don't know if there is/was a reasonable alternative (there probably isn't really), but I guess at this point the only thing to do is to write better documentation/guides on how to use CORS.
Apparently to work around some bug in Microsoft ISS server. But this has also greatly increased the performance hit from CORS under some common usage scenarios, which is sad.
http://lists.w3.org/Archives/Public/public-appformats/2008Ma...
It can still be abused to exfiltrate data.
Whenever your browser sends any request to any site, it sends the cookies(/other auth data) associated with that site along with it. In other words, cookies are fundamentally just associated with the receiver, not the sender. So the "solution" is for the receiver to block requests from the wrong sender, since otherwise any site could send authenticated requests to any random site. [Edit in response to comment below: I should've mentioned more here, but I understand what happened was browsers introduced the Same-Origin Policy to prevent this from happening, and introduced CORS as a dynamic bypass mechanism for that, which, unless you implement OPTIONS, can get you these half-baked insecure situations where requests still get sent and executed, but the client JS doesn't get to see the responses.]
To me this whole mess is stupid because the premise shouldn't be true in the first place (why the heck should a cross-origin request send auth data? cookies etc. should be "contained" to whatever domains the original site restricted them to), but that's apparently The Way Things Are, and so here we are: browsers do something completely unexpected and insecure, and we blame devs for getting caught off-guard and not protecting against a security hole browsers introduce.
(Yes, I have Opinions on this. Please tell me exactly where I'm wrong, because I suspect I might be, but I have yet to figure it out.)
Before the client does the request for data, it does a preflight request via OPTIONS to determine what is allowable (is this domain allowed, this type of call).
If it is allowed, it can send and request cookies on the requested domain in the current standard.
However, this is not supported in older browsers, so the request will just work.
In newer browsers, without the preflight check, the server still receives and replies to the request, but the reply is ‘ignored’ by the browser and throws an error.
The server still sees the request, so the data can be exfiltrated.
In terms of backwards compatibility, it is actually the opposite. Newer browsers will block stuff that worked in older versions.
https://github.com/OWASP/CheatSheetSeries/blob/master/cheats...
(as you mentioned, backwards compatibility requires that this is opt-in when the cookie is set, not opt-out)
(P.S. the way you phrased the request handling would violate causality, so I'm assuming you were referring to the OPTIONS check beforehand...)
Not necessarily true. You can have Javascript running on evil.com that submits a form to banking.com and it will send your session cookies for banking.com along with the request. It's just that evil.com can't read the response content.
I'll admit I may be one of the developers that doesn't understand CORS...
See https://developer.mozilla.org/en-US/docs/Web/Security/Same-o...
This is why we have CSRF tokens. So that you can effectively block Cross-Origin writes.
But yeah, you can't just make arbitrary requests like this with XHR
If you just create an HTML form with the bank as action, fill in the inputs and submit via JS, no, because that's _navigating away_. This is also why GET requests that perform actions are dangerous, you can just embed them as an image to provoke a request. Which is exactly how zoom built their API. (they also then measured the size of the image to determine the response code, which is.... inventive)
CORS was introduced as a way to allow requests that browsers used to not allow at all: things like cross-site XHR in the first instance. Then it was expanded so that requests that are not normally subject to CORS checks (image loads, script loads, stylesheet loads) could opt-in to being subject to them, for various reasons. The default for those loads is still "no CORS". And there still isn't a way to do a navigation subject to a CORS check, even with opt-in.
Disclaimer: I work on Gecko and I've reviewed/implemented parts of the CORS spec.
There's a second problem CORS kinda tries to solve, which is the ambient authority problem: services that run behind firewalls and assume that if someone can reach them the someone should have access. If someone runs a browser behind the firewall and opens a page on unsafe side of the firewall, that page can then issue network requests from the browser and thus end up access things on the "safe" side of the firewall. This is a large part of why CORS has the whole preflight complication and the rules around when preflights happen: the idea is that in this situation just making the request, not even receiving a response, is potentially damaging. There are the carve-outs for requests that could be generated without things like XHR that are subject to CORS (e.g. by doing a form submission or <img> load or whatnot); if your ambient-authority-using server responds in interesting ways to those, CORS is not going to help you... The _right_ fix for this stuff, of course, is for services to stop using ambient authority and/or for browsers to block requests from public sites to private IPs. Unfortunately in practice detecting "private IPs" reliably is not trivial, because fundamentally it depends on the routing and firewall topology, which the browser doesn't really know about.
https://en.wikipedia.org/wiki/DNS_rebinding
> The NoScript extension for Firefox includes ABE, a firewall-like feature inside the browser which in its default configuration prevents attacks on the local network by preventing external webpages from accessing local IP addresses.
They do block certain ports that are known to be problematic (25, 6667, 5222, etc)
https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/Web...
I haven't been following this closely, but for Firefox https://bugzilla.mozilla.org/show_bug.cgi?id=354493 is the relevant bug, with some (failed) attempts to do that.
So, the one domain will attempt to communicate, the other domain will receive the request and return a response.
If the client doesn’t ‘like’ the response, it will error.
So in fact CORS without any server-side configuration allows one to do arbitrary GET requests with cookies and authentication through XHR, but you could always just dynamically create IMG (as in the Zoom kludge) or SCRIPT (and the response will be executed in the page context, which is huge security risk as well as how JSON-P works) tag.
In other words: if your application does something security-sensitive (ie. write to anything other than log file) as part of GET request apart from producing the response, then your application is broken and same-origin policy nor CORS has nothing to do with that.
application/x-www-form-urlencoded
multipart/form-data
text/plain
https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#Simpl...
onPageLoad()
{
for ( x = 1; x < 10000; x++ )
{
load_page(https://facebook.com/newpost?to=world&message="My mother's face resembles an unwashed buttocks");
}
}By default, the same-origin-policy doesn't allow all requests to other origins (domains/websites/ports). But if you want to allow another website to send requests to your site you can use CORS to do so.
So for example, if you have a contact form on one page (example.com) and the API for processing the submitted forms on another domain (processing.com; different origin), the receiving server tells the browsers via CORS that submissions from example.com are allowed.
Yet another article about some <acronym> ranting about usage and misconceptions about <acronym> without even caring to explain in the first 20 paragraphs what the hell <acronym> mean or is supposed to achieve in the first place.
You are free to re-use and adapt this sentence for each new <acronym> like feature trending at any point in time.
PS: My bad it was actually described in the last sentence of the citation inserted in the third paragraph.
Edit: It works with image elements!
img = document.createElement("img")
img.src = "http://localhost:1337/service"
Btw, CORS is a PITA when doing pure web apps, apps that doesn't requre a server. I recently made a RSS reader web app, but had to use a CORS proxy.I wonder if there is any way I can use an image element to bypass CORS and read RSS xml that way !?
new Image(1,1).src = 'http://localhost:1337/service'The article in question says they used the image dimensions to encode the error codes returned by the server when making a request.
I can imagine you could simply encode the payload in the dimensions of the image.
If you create a single-purpose webserver, you can really handle this better than asking the client to play nice via an HTTP header.
Yes, the server can drop the request or return an error, but that's just half of it. If it wants the browser to accept the request, it has to explicitly say it by using the CORS headers.
I guess because we don't trust servers to do that, we need "don't allow by default, let the server opt-in" instead of "allow it by default, let the server opt-out", because too many servers would not opt out.
> Servers can also notify clients whether "credentials" (including Cookies and HTTP Authentication data) should be sent with requests.
What are you going to do? Some things are just complicated and it's hard to find good people who actually bother to understand these systems, it's what separates the best from the rest.
Relatively recent Chrome debug messages about a no-cors "opaque" option, suggest the client has the ability to bypass the server rules without using any kind of proxy.
Is this true? Maybe I didn't notice it got added as an option when fetch was added in addition to the old style XHR requests?
I've tried to get my head around CORS a few times without much luck.
Is this server started by Zoom? In order to implement an endpoint, they’d have to ship a server along with the client. And this server would always be running? Is this not overkill for such a small feature (being able to click a link)
The author is explaining how they could have done it more securely. I think everyone here including the author would agree it's a ridiculous solution for this "feature".
That's just my opinion on it.
ALL DEVELOPERS DON'T UNDERSTAND
Another turd in the tower of shit that is JavaScript.
Now that we've established that by default a.com cannot read a response from b.com , CORS allows b.com to relax this restriction so that a.com can read from b.com . This allows one website to communicate back and forth to the server of a different website, making certain APIs easier. I don't consider that useless.
I mean, the biggest problem the SOP solves is that some website could trick the browser into sending an authenticated request to your site/API. And while the SOP just kills interactions between different origins completely, I wonder why they didn't just go with not allowing the browser to include any state it got from an origin earlier when the request comes from a different origin. That way it would be possible to do requests between different origins, but without the problem of hijacked authentication.
Instead, we got this same origin policy which completely isolates different origins and makes browsers a lot less powerful than other HTTP/S clients and drives developers mad.
Edit: Feedback appreciated.
I think it's because people intuitively think of access control as an answer to the question "Who are you?", which means your authentication credential needs to be sent with every request to a given site.
The alternative solution is to use "capabilities" which are a way of accessing a given resource by the very fact that you possess a reference to it. E.g. the google drive feature where you can say "anyone with the link can {view,edit,comment,etc}".
The downside is obvious though: it would require everyone to adapt to this model, and rewrite all of their apps to use it instead of the session cookie model. Not gonna happen without a massive effort (see ipv6 rollout for an example of the effort required for something like this).
After all, cross-origin requests are a normal thing on the web. The problem is that browsers make credentials available to websites that shouldn't have control over them. It is like removing all doors from a house because otherwise, the stupid neighbor would give the keys (you gave him for watering your plants) to anybody that would ask.
For example we built https://yang2020.app/meeting
You can have stuff like that yourself, for free, no Zoom required. It’s open source
SPECIFICALLY what does Zoom provide? People can install wordpress easily, and 30% of the Web has. Why not for videoconferencing?
It took us about 3 months of work to get all the quirks out, but anyone can do it. A developer can grab our library but if you don't know how to code, it's just a widget you get off the Internet. And it works on YOUR WEBSITE. This isn't like Dropbox because there is no desktop app.
How long do you think it would take a non technical team or company to get it up and running?
It took us 3 months to turn WebRTC into something that “just works” on any website. The RESULT OF THAT is now available for anyone to use.
People can have it up and running in 5 minutes. On their own website.
Users have nothing to download. It just works, including on mobile web browsers.
Companies can also put it into their mobile apps.
So what is the downside again?
Perhaps one advantage of Zoom is that it rebalances things onto servers it owns eg via websockets? Is there documentation on this?
> An external signaling server should optionally be used for larger installations. Leave empty to use the internal signaling server.
> Please note that calls with more than 4 participants without external signaling server, participants can experience connectivity issues and cause high load on participating devices.