
Developers don't understand CORS - chrisfosterelli
https://fosterelli.co/developers-dont-understand-cors
======
cakoose
The blog post author doesn't understand CORS either! Their advice on how to
fix the problem is wrong:

> 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"](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.)

~~~
chrisfosterelli
The advice isn't wrong, but you did misunderstand it. The CORS header will
definitively block the request to an AJAX REST API from going through, because
it will be a POST request with an `application/json` Content-Type, which will
trigger a preflight request.

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.

~~~
comex
You shouldn’t assume that a post titled “Developers don’t understand CORS”
will only be read by people that understand all the intricacies of CORS. :)
For everyone else, the need for a POST request [edit2: as well as a non-form
Content-Type] may not be immediately apparent.

(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.)

~~~
chrisfosterelli
Haha true! It's tricky to find a balance between a short post that nails home
a point and a complete guide that hits all the nuances. I definitely intended
the former here but I think this is good evidence there is room for the later.

~~~
spydum
i think the tragedy of web security is that, it's become too complicated to
provide simple guidance to developers who are feature-focused. They don't want
to become experts in security-header-machinery and crazy amount of domain
knowledge needed, but there aren't many other options.

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.

~~~
heyoni
Honestly, I think the real issue is being able to see exactly what the
machines are doing when they talk to each other. And I don't mean diagrams
either.

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.

~~~
freeone3000
Chrome Developer Tools shows the preflight request in the network tab, as it
does every other request. It's even called out as being part of the same
communication, if you group by communication instead of sorting by timestamp.
What's not clear is why it's necessary sometimes and why it's not, where you
have to dig into a mix of history and security policy, and do a bit of threat
modelling.

------
csours
I certainly don't understand CORS. I've read about it several times and I
don't remember what I read. It's like CORS has a teflon coating that prevents
it from sticking in my mind. I know what CORS is for and why you need it, but
I have no idea how, where, and when to use it.

~~~
WhitneyLand
This Teflon you're talking about, it's a thing. Not just with CORS, not just
with you.

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...](https://stackoverflow.com/questions/1381097/how-do-i-get-the-name-of-
captured-groups-in-a-c-sharp-regex)

[2] content provided under MIT license.

~~~
dkarl
I call it my intellectual immune system, but it might be more like a runaway
process killer or an OOM-killer. It hates ugliness, and it hates complexity. I
kind of appreciate its attempts to clean Javascript out of my brain. Even
though it makes it frustrating to do front end tasks, I appreciate the good
intentions.

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.

~~~
adtac
As an online discussion grows longer, the probability of an unrelated analogy
involving computer science and software engineering approaches 1.

I shall graciously accept this being called adtac's law out of sheer humility
:P

~~~
dkarl
More like: people rely on shared knowledge when communicating. This _is_
Hacker News, after all.

------
jonstaab
Totally agree. Something about CORS and the resources out there makes
newcomers think that it's something the client has to do differently. I
thought this when first doing cross-origin stuff, and thought it was just me
until my dad (programmer for 30 years) got stuck on it the exact same way.

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.

~~~
corey_moncure
I for sure haven't mastered CORS.

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?

~~~
jonstaab
I'm not familiar with Blazor or WebAPI, but if you're not expecting requests
from client-side javascript specifically, you can just use `Access-Control-
Allow-Origin: null`, since non-browser clients don't respect CORS anyway.

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.

~~~
theturtletalks
Can you point me to some documentation for dynamically allow-origin header?
I’m working on open sourcing our frontend and so if users have their own
frontend on their own domain, how do we allow those calls to our backend with
CORS? If we turn CORS off, is this a security issue? From the frontend, we
send a JWT with the header and check this for protected routes on the backend.

~~~
jonstaab
CORS is really pretty simple, it's getting the threat model that is tricky.
Some docs on Allow-Origin here: [https://developer.mozilla.org/en-
US/docs/Web/HTTP/Headers/Ac...](https://developer.mozilla.org/en-
US/docs/Web/HTTP/Headers/Access-Control-Allow-Origin) and a more complete
walkthrough here: [https://developer.mozilla.org/en-
US/docs/Web/HTTP/CORS](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS)

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.

~~~
politician
It's not fair to call Dynamic Allow-Origin simple. This is super tricky and
nonstandard usage that is only necessary to workaround the fact that browsers
do not support multiple values on the Allow-Origin header even though the spec
allows it.

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.)

~~~
blackflame7000
Realistically, Dynamic Allow-Origin is the only way when you want to add more
than a few allowed Origins. You wouldn't want to send back a 10kb header
detailing all the clients of your service, would you?

~~~
politician
Call it a bug in the spec if you want, but regardless the spec provides no
guidance about whether reflecting the Origin is a good workaround.

~~~
blackflame7000
You shouldn’t reflect the origin unless it matches your whitelist. If you
wanted to allow all you would just use *. If its not allowed you should return
invalid headers instead. Thats why its dynamic

------
Timpy
> Original security article: briefly touches on CORS

> 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.

~~~
calibas
I think the issue with understanding CORS is that you first need to understand
same-origin policy and exactly when it applies. CORS is simply a method for
bypassing same-origin policy. You also need to understand how a CSRF attack
works.

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.

------
edejong
The reason the developers didn't use CORS is because active mixed content
isn't allowed. You simply cannot call '[http://localhost'](http://localhost')
from a https domain. For a medical SaaS application which needed to
communicate via USB I created a self-signed certificate during installation
and added it to the trust store to be able to call active content on
localhost. Previously, we used NPAPI (which, when used correctly was more
secure than the current plugin architecture), but that got deprecated.

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).

~~~
chrisfosterelli
This is discussed in the post text, but happy to elaborate here in more detail
here! Here is the patch links from Firefox[0] and from Chrome[1] which they
specify that active mixed content policies do not apply to the localhost,
because the w3c specification was updated to specifically allow this
behaviour[2]. You might have to use 127.0.0.1 directly. So yes, it is allowed.

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](https://bugzilla.mozilla.org/show_bug.cgi?id=903966)

[1]:
[https://chromium.googlesource.com/chromium/src.git/+/130ee68...](https://chromium.googlesource.com/chromium/src.git/+/130ee686fa00b617bfc001ceb3bb49782da2cb4e)

[2]: [https://github.com/w3c/webappsec-mixed-
content/commit/349501...](https://github.com/w3c/webappsec-mixed-
content/commit/349501cdaa4b4dc1e2a8aacb216ced58fd316165)

~~~
edejong
I see it is fixed now. I thought up this work-around back in 2014, when NPAPI
was phased out. Spotify and another large player came up with similar
solutions.

~~~
chrisfosterelli
Totally. I do get the constraints and I imagine Zoom had similar thinking
here. The image approach, although hacky, can even be secure as-is if they
need it for backward-compatibility but it needs to check the headers and not
honour requests with the wrong value.

~~~
edejong
I kind of like the ingenuity of the approach. Never occurred to me to use non-
active content.

------
jrochkind1
I find in most discussions of CORS, developers don't understand the _threat
model_ it is meant to be guarding against.

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.

~~~
pawelk
I met many developers who thought that CORS can guard access to API endpoints,
completely missing the fact that one can just use a client that doesn't follow
CORS.

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.

------
p2detar
Recently, I was working on an issue where our new frontend client was not
receiving custom response headers from the server API. It turns out I had to
expose headers first using 'Access-Control-Expose-Headers' [0]. Neither me,
nor the frontend dev have heard of that response header before and we thought
we knew something about CORS.

[0] [https://developer.mozilla.org/en-
US/docs/Web/HTTP/Headers/Ac...](https://developer.mozilla.org/en-
US/docs/Web/HTTP/Headers/Access-Control-Expose-Headers)

------
jusob
Yet another explanation of CORS.

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.

------
wwarner
Best CORS reference is MDN [1].

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](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS)

[2]
[https://stackoverflow.com/a/23549398](https://stackoverflow.com/a/23549398)

[3]
[https://bugs.chromium.org/p/chromium/issues/detail?id=131368](https://bugs.chromium.org/p/chromium/issues/detail?id=131368)

------
zenexer
The author suggests that using CORS to allow
[https://zoom.us](https://zoom.us) with
[http://localhost:19421](http://localhost:19421) is possible. This is
factually incorrect. Mixed content policies prevent the http: origin from
communicating with the https: origin.

 _" 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.

~~~
chrisfosterelli
This is discussed in the post text, but happy to elaborate here in more detail
here! Here is the patch links from Firefox[0] and from Chrome[1] which they
specify that active mixed content policies do not apply to the localhost,
because the w3c specification was updated to specifically allow this
behaviour[2]. You might have to use 127.0.0.1 directly. So yes, it is possible
and not factually incorrect.

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](https://bugzilla.mozilla.org/show_bug.cgi?id=903966)

[1]:
[https://chromium.googlesource.com/chromium/src.git/+/130ee68...](https://chromium.googlesource.com/chromium/src.git/+/130ee686fa00b617bfc001ceb3bb49782da2cb4e)

[2]: [https://github.com/w3c/webappsec-mixed-
content/commit/349501...](https://github.com/w3c/webappsec-mixed-
content/commit/349501cdaa4b4dc1e2a8aacb216ced58fd316165)

~~~
zenexer
I'm fairly certain that these patches didn't land until recently, at which
point the design decisions were probably already made at Zoom. (I don't know
much about Zoom, but that seems like a reasonable assumption.) Additionally,
these changes aren't likely to apply to Firefox ESR or IE for a while.

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.

~~~
chrisfosterelli
These are from years ago, and as mentioned there are two other alternatives
discussed. There's no excuse to not checking origin at all. They could have
even used the image hack and then checked the origin.

------
zer0faith
Rarely does a application actually need to enable CORS. If all of your
webcalls are from the same domain YOU DONT NEED CORS. (Chatbots/socket.io)

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...](https://www.moesif.com/blog/technical/cors/Authoritative-Guide-to-
CORS-Cross-Origin-Resource-Sharing-for-REST-APIs/#)

~~~
mderazon
Not so rarely. It's pretty common to serve the js frontend code on one domain
the apis on a different domain

------
MrStonedOne
The number one issue with CORS I've found is that merely defining it undoes
alot of defaults and most of the top searches on the subject of how to do x in
CORS doesn't explain what those defaults are. So you get devs breaking things
trying to whitelist something not normally allowed then deciding to cast a
wide net to fix those things.

The CORS standard needs to be radically redefined from the ground up with
developer ease of use as part of the consideration.

------
martin_a
At this point I don't want to understand CORS.

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.

~~~
cooperaustinj
Use a CDN!

~~~
martin_a
So my scripts will randomly go down with the rest of the internet? Great idea!

------
systematical
Thesis: Developers don't understand CORS. So I read it. All along the author
does not attempt to explain CORS to me. I then Google CORS, and think I do
understand it already, but this guy is telling me I don't, but completely
fails at explaining why I don't. Maybe I don't understand CORs, but the author
didn't help anyone with this piece. This received 508 upvotes...how?

------
garganshum
This is my high level overview of CORS.

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.

~~~
greenshackle2
Mostly correct, but the browser may or may not send an OPTIONS request
depending on the request type, headers, and more.

[https://developer.mozilla.org/en-
US/docs/Web/HTTP/CORS#Simpl...](https://developer.mozilla.org/en-
US/docs/Web/HTTP/CORS#Simple_requests)

------
mrwww
Many beginner tutorials on web development breeze by "how to disable cors" as
the most natural obvious thing to do, usually when wanting to call your own
API on a subdomain. I think because it sometimes "gets in the way" and how to
properly work with it requires slightly more planning than just disabling,
developers fall into bad habits.

------
hn23
From the title I expected a simple explanation how CORS works...my fault..

------
sirsuki
To be that guy... forget CORS. All localhost servers have a pinned public key.
The zoom.us site has the private key. It passes along a signed request. The
local server then validates the signature! How is that so hard?

Oh wait, I just described CSRF!! Zoom, here is your egg. Now promptly smash it
on your face; thank you.

~~~
Thorrez
Wouldn't that allow the token to be replayed? Attack scenario:

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).

------
Animats
One of the side effects of CORS is that you can't display most off-site images
through WebGL. Displaying an off-site image through <img> is allowed.
Otherwise ads would break. But you can't bring the same image into the WebGL
system.

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.

~~~
squiggleblaz
The statement is false. CORS - cross origin request sharing - only adds
permission. CORS could allow you to display off-site images through WebGL, but
it cannot prevent it.

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.

------
rmbryan
Maybe we don't understand CORS.

Here's a fact-based starting point to learn about CORS:
[https://en.wikipedia.org/wiki/Cross-
origin_resource_sharing](https://en.wikipedia.org/wiki/Cross-
origin_resource_sharing)

------
ozim
That is not about CORS it is about "DevOps", implemented in a way: let just
developers do what they do on their local machine so we don't spend money on
Ops people and security people because our developers are smart enough, we pay
them 400k a year.

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.

------
Waterluvian
I'm in this semi-special place in web dev where almost everything I make is
for a captive audience on local networks. So it feels like there's entire
segments of web dev that I've only learned to subvert. CORS is one of them.

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.

------
anaphor
That is because CORS is a kludge to work around the fact that we decided to
use session cookies "because developers understand it easily" instead of just
re-doing everything.

See [https://w3c.github.io/webappsec-cors-for-
developers/#cors](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/](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.

~~~
stefan_
Wrong conclusion. The kludge is making cross origin requests in the first
place.

~~~
anaphor
Maybe, but people want to write applications that can easily interact with
each other in a secure way. Trying to do that when your browser implicitly
authorizes you no matter what the origin of a request is makes that
extraordinarily difficult to do safely. Adding in the backwards compatibility
problem makes it even worse because any solution is going to have to work with
the existing methods.

------
jedberg
It's not just a lack of understanding but a lack of support in server tooling.
I was setting up CloudFront on AWS and kept running into CORS issues. I did
all the steps to set the necessary headers, but it still didn't work. I ended
up just doing some layer 7 routing to route requests to /api towards the other
server that was on the other domain to avoid the CORS issues, because now
there is no cross domain request. It seems more secure that way anyway.

~~~
icebraining
But were the headers reaching the browser or not? CORS is something strictly
for the browsers to parse, so if it's reaching the browser, either the policy
is wrong (and you can usually see that in the browser console, when it blocks
the cross-domain request) or it's not a CORS problem.

~~~
jedberg
It wasn’t reaching the browser.

~~~
icebraining
Seems like you have to whitelist the headers:
[https://docs.aws.amazon.com/AmazonCloudFront/latest/Develope...](https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/header-
caching.html#header-caching-web-cors)

~~~
jedberg
I did, but it still didn't work. It was extra complicated because it was
coming out of API gateway calling Lambda.

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.

------
chpmrc
How does not allowing CORS make any sense if any other application running on
my system can bypass it except the browser? _The_ technique to bypass it is to
have a proxy, either local, or remote. Wouldn't it make more sense to simply
allow the browser to make any request the app wants?

~~~
chpmrc
Even Cordova (which basically runs a web view on mobile) has a plugin to
bypass CORS ([https://hackernoon.com/a-practical-solution-for-cors-
cross-o...](https://hackernoon.com/a-practical-solution-for-cors-cross-origin-
resource-sharing-issues-in-ionic-3-and-cordova-2112fc282664)). Are we entirely
sure that the benefits of disallowing CORS by default outweigh the annoyances?
Is there any study on this?

~~~
squiggleblaz
The point of CORB (i.e. cross origin request blocking i.e. what browsers are
doing i.e. what CORS relaxes) is effectively to stop authenticated requests
from going from a browser with multiple authentications to a protected server
on the basis of a request produced by third party.

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.

------
jrudolph
CORS is a minefield. It doesn't help that the RFC is incredibly dense. I may
be totally missing something, but I've repeatedly wondered though if CORS
still has its place in times of SPAs that exclusively use xhr + javascript +
some sort of auth token talking to a REST API?

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.)

~~~
untog
> Remove cookies and basic auth headers, what's the point of CORS?

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.

~~~
throwamay1241
I don't think the act of sending the POST is blocked by CORS, receiving the
POSTed response data is though.

------
hardwaresofton
Security practices that are hard to use correctly are often misused/not used
at all can be worse than if they weren't there at all (because of the false
sense of security). On this axis, and the axis of adoption CORS is a huge
failure (despite the fact that when used correctly it does increase security).

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.

------
sandstrom
An annoying thing with CORS is that one cannot allow all paths for an origin.
I.e. there will be pre-flight requests for every new path that requests are
made to in a REST API, making the response caching useless.

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...](http://lists.w3.org/Archives/Public/public-
appformats/2008May/0039.html)

------
thrusong
I sort of get it and yet even when I'm sure I've configured it correctly, I'll
hit road blocks in Safari, for example, when it works everywhere else as
expected. It is such a frustrating experience.

~~~
neuroticfish
I'm dealing with frustrating CORS issues right now. We have a CDN intercepting
outgoing requests from a request resource and nobody here can seem to figure
out how to get it to keep allowed headers for OPTIONS method requests.

~~~
anderspitman
Sounds like something that's going to have to be configured on your CDN. I'm
assuming you already checked with them/their docs?

------
phkahler
Not a web dev. Can anyone tell me what CORS is ultimately used for? Not the
literal technical details, the higher level what does it enable sites to do?
And is that for them or their visiters?

~~~
mehrdadn
Also not a web dev, but if I'm remembering it correctly (someone please
correct me if I'm wrong), the way I understood it was:

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.)

~~~
pixelperfect
I'm pretty sure you're incorrect - requests to sites different from the one
you are currently on will not include auth data by default because of the
same-origin policy. The auth data would only be included if the server that
the request is being sent to responds with an Access-Control-Allow-Origin
header whose value matches the origin the request was made from.

~~~
anaphor
> I'm pretty sure you're incorrect - requests to sites different from the one
> you are currently on will not include auth data by default because of the
> same-origin policy

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.

~~~
pixelperfect
I thought the browser at evil.com would first send a pre-fetch request without
the cookies, but not send the full request when it doesn't get the correct
value from Access-Control-Allow-Origin in the response from banking.com.

I'll admit I may be one of the developers that doesn't understand CORS...

~~~
anaphor
I think you could implement that with CORS + CSRF tokens (blocking all types
of Cross-Origin requests) but the default behaviour is to allow Cross-Origin
writes (e.g. with form submissions).

See [https://developer.mozilla.org/en-
US/docs/Web/Security/Same-o...](https://developer.mozilla.org/en-
US/docs/Web/Security/Same-origin_policy#Cross-origin_network_access)

This is why we have CSRF tokens. So that you can effectively block Cross-
Origin writes.

~~~
pixelperfect
I see what you mean, evil.com could still make a request that includes
cookies, which is why we need CSRF tokens. But from my understanding, it
wouldn't be able to do that in a XMLHttpRequest hidden on the page. It would
have to be a request from something like submitting a form which would
navigate the user off the page. Is that correct? Of course it doesn't make
much difference from a security perspective.

~~~
anaphor
You can work around that by submitting it within an invisible iframe element,
e.g.
[https://stackoverflow.com/a/17953761/903589](https://stackoverflow.com/a/17953761/903589)

But yeah, you can't just make arbitrary requests like this with XHR

------
Twisell
My point of view as a SQL developer reading a article about something front-
end by curiosity:

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.

------
z3t4
Just tried and I get CORS errors when trying to access localhost in Firefox,
also on images via xmlHttp ...

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 !?

~~~
heavenlyblue
>> I wonder if there is any way I can use an image element to bypass CORS and
read RSS xml that way !?

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.

~~~
z3t4
I was thinking about using img.src to replace xmlReq and Fetch. So that I can
fetch sites that I do not own. eg. RSS feeds.

~~~
heavenlyblue
So how would you receive the data, then?

------
Beldin
_The webserver listening on localhost:19421 should [...] set a[n] Access-
Control-Allow-Origin header with the value[https://zoom.us](https://zoom.us).
This will ensure that only Javascript running on the zoom.us domain can talk
to the localhost webserver._

If you create a single-purpose webserver, you can really handle this better
than asking the client to play nice via an HTTP header.

------
arve0
Zoom does not need CORS-headers to fix the vulnerability, rather make the web
server running on localhost drop requests with wrong origin header.

~~~
jrochkind1
Hmmm.... never thought about this but now thinking about it... does _anyone_
need CORS? Can server-enforced access controls on "Origin" substitute for CORS
in all cases?

~~~
mderazon
The server responsibility of CORS is also about making sure the browser
accepts the cross origin request (in case it's a valid origin). If it won't
send the correct headers back, the browser will drop the request

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.

~~~
jrochkind1
Ah, right. But why not have browsers allow cross-origin requests, but let an
individual server just deny it based on origin header?

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.

------
khnov
From what I see, the author do not understand CORS neither ! He blames pple
and do can't even give the explanation of CORS using his words

------
DGAP
This article is a great illustration to me as well of how hard it is to do
security right even if you're a professional, much less a developer who's
mostly responsible for implementing features not building secure systems. The
internet is rife with self-proclaimed experts, all offering directly
contradictory advice as loudly as possible on best practices for security.

------
bamboozled
Most developers I interact with don't even understand something as fundamental
how a TCP connection works (not blaming) let alone something as abstract as
CORs.

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.

------
WhitneyLand
Bypassing it all (be careful) with no-cors?

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?

------
DrOctagon
If an HTTP API is GET request only and there is no authentication for the API,
think public weather info API, is there any risks with having "Access-Control-
Allow-Origin: *" set?

I've tried to get my head around CORS a few times without much luck.

~~~
syntheticcdo
ACAO:* is appropriate for a publicly accessible, unauthenticated request.

------
felxh
I think the advise in the article and various comments here won't work, for
the simple reason that Safari does not allow ajax requests from non localhost
origins to localhost. I suspect this is the real reason they used an image
request.

------
poglet
The Firefox extension "CORS Everywhere" has let me test my web app on my
computer without having to mess around with headers. This was while using
PowerShell Polaris to create a website.

------
nottorp
Would it have been so hard to spell out what CORS means? So people who don't
do web development don't have to wade through the acronym soup?

------
patrickaljord
Developers understand CORS, it's just like any security measure, it goes
against the infinite list of features devs are pressured to implement.

------
alexchamberlain
I don't really understand how CORS adds much security wise; it relies on the
web browser behaving and respecting your policy...

~~~
brlewis
It protects against attacks where the bad actor is trying to get _your
browser_ to do something you don't want. It's OK for browser security measures
to rely on web browsers to implement them.

------
raxxorrax
Wouldn't this be stopped by address translation by routers for normal users?
Don't know much about the app itself...

------
microDude
One thing I never understood was why server1.mycompany.com (origin) cannot
make a GET request to server2.mycompany.com?

------
ops4c0d3
One of the most enlightening threads I've come across in awhile. Thanks for
the solid explanations

------
mrmonkeyman
The browser rejects it sure. But what if I write my own custom nonstandards
compliant browser?

------
MetalGuru
> The webserver listening in on localhost:19421 should implement a REST API

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)

~~~
greenshackle2
They already ship a server along with the client. It's already always running.

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".

------
formatkaka
Were we supposed to understand it? Voodoo programming is the solution to CORS.

------
vkaku
CORS and preflighting, ugh, just adds more developer friction.

That's just my opinion on it.

------
akidomowri
> A security guy isn't 100% correct

ALL DEVELOPERS DON'T UNDERSTAND

------
greg7mdp
dude, I've been a developer for over 30 years and I haven't ever heard of
CORS. Let alone understand it.

~~~
miguelmota
Well just until 2014 CORS became a W3C recommendation. Before that CORS was
mainly in Draft mode and loosely supported by browser vendors. I do find it
surprising that you've never dealt with CORS in the last five years if you've
ever deployed any production grade API or web service.

------
thundergolfer
C -

------
kyberias
Very arrogant post that doesn't explain CORS or offer any useful links for
understanding CORS.

------
simplecomplex
CORS is useless, unnecessary, insecure, and essentially only serves to annoy
developers. Par for the course for all web tech from the last 20 years.

Another turd in the tower of shit that is JavaScript.

~~~
Thorrez
If attacker.com makes a request trying to read bank.com/my-bank-account-number
should attacker.com be able to do that and read the response? The same origin
policy blocks the response from being read.

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.

------
arendtio
I absolutely hate the current same-origin-policy (SOP) we have and therefore
CORS. In the end, CORS is a way to work around the problems the same origin
policy creates. Yes, I know there are good reasons why we have it, but in my
opinion, it is the wrong solution to that problem.

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.

~~~
anaphor
Not sure why you're getting downvoted. It's a valid point.

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).

~~~
arendtio
Thanks for the response. Yes indeed, there would be the cost of change. But if
we want the web to be truly decentralized it doesn't make much to disallow any
cross-origin interaction by default.

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.

------
EGreg
Seriously wondering! Why is Zoom worth $2B and companies are paying per minute
plans to host meetings when you can now host your own videoconferencing
software easily on your own website or app or intranet using WebRTC, branded,
with your own experience, widgets, and it can all be open source? All you need
to pay for are dumb TURN servers eg from twilio. Plus it would be far more
secure.

For example we built
[https://yang2020.app/meeting](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?

~~~
leesec
This reads so much like the infamous dropbox comment.

~~~
EGreg
How so? It's super easy to just download and put into any website. Look at the
demo above.

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.

~~~
leesec
So you're wondering why people use something that just works over something
that took you (assumedly someone technical) 3 months to get all the kinks out?

How long do you think it would take a non technical team or company to get it
up and running?

~~~
EGreg
No. You’re just mistakenly comparing apples and trees on which apples grow.

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?

