Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Fetch API; why use the "no-cors" mode if the response is Opaque?

Tags:

cors

fetch-api

I read this question and this question . The former only explains 'no-cors' vs 'same-origin'; the latter recommends that 'no-cors' is not useful because of the Opaque Response (which Javascript cannot read/do anything useful with):

You basically never ever want to use mode: 'no-cors' in practice — except in some very limited cases. That’s because what setting mode: 'no-cors' actually says to the browser is, “Block my frontend JavaScript code from looking into the contents of the response body and headers under all circumstances.” In most cases that’s obviously really not what you want.

Can someone advise what are examples of these "limited cases" where we would want to use "no-cors" (even though the response is Opaque?)

The only case I can think of is a sort of one-way communication; if it is sufficient for the client to send a GET or POST to the server, simply so the server can track that the request happened; (for example, to increment a request counter); ...

...then it is sufficient for the response to be an OpaqueResponse; i.e. the client only need to know if the request was successful (status 200), doesn't expect any payload response.

Is my idea a valid example? Can someone recommend other possibilities / use-cases / examples of 'no-cors' usage?

like image 778
The Red Pea Avatar asked Sep 29 '18 15:09

The Red Pea


People also ask

What does no-CORS mean in Fetch?

no-cors. Prevents the method from being anything other than HEAD , GET or POST , and the headers from being anything other than simple headers. If any ServiceWorkers intercept these requests, they may not add or override any headers except for those that are simple headers.

What is an opaque response CORS?

An opaque response is for a request made for a resource on a different origin that doesn't return CORS headers. With an opaque response we won't be able to read the data returned or view the status of the request, meaning we can't check if the request was successful or not.

What is mode in Fetch?

mode. The mode option is a safe-guard that prevents occasional cross-origin requests: "cors" – the default, cross-origin requests are allowed, as described in Fetch: Cross-Origin Requests, "same-origin" – cross-origin requests are forbidden, "no-cors" – only safe cross-origin requests are allowed.


2 Answers

Remember that CORS settings doesn't prevent a request from reaching the server - that's what authentication and CSRF is for. Instead, it prevents responses from being read by the page. The request is still:

  1. sent from the page,
  2. the browser adds the Origin header,
  3. it's still processed by the server (generally - CORS has nothing to do with this, although there maybe other security in place),
  4. and when returned the browser checks the response header for Access-Control-Allow-Origin. If it matches what the browser thinks is the Origin then the browser lets the page see the result of the request.

That's the key - Same-origin policy and CORS settings disallow the page in a cooperating browser from seeing the response.

Note also there is a step 0. above, which is an OPTIONS "pre-flight check" is sent to check that if the request was to go through would the page be allowed to see the result? If not, then they assume there is little point in sending the request - but that is an assumption.

Now to address the question

mode: no-cors does two things:

  1. It says that I don't need to see the result
  2. and so it doesn't send the pre-flight check

Off the top of my head, here's what I think I could use it for (some of them nefarious)

  • Any time when I don't need to see the response, such logging, tracking, or hacking; when the frontend code is essentially a try { const notNeeded = fetch(...) } catch { console.log('Tough luck, do nothing') }
  • Any time I want to send the data to the server as fast as possible by not sending a preflight check. I can always send a GET with CORS later on to read the data when I really need it.
  • Caching, detailed in this answer

Reminder

CORS does the above. It doesn't do the following:

  • Prevent the server from processing the request - that's what Authentication and CSRF prevention is for.
  • Stop spoofing the Origin header - this works with only cooperating browsers. And as an attacker, you generally don't have access to what browser a user is using. Because the header can be spoofed, a server shouldn't use the data in it for security. (And this is why when testing browser APIs through a tool like CURL/Postman/Insomnia you need to check CORS headers are coming through, because they accept all responses and CORS policies are never applied.)
like image 170
eedrah Avatar answered Oct 14 '22 00:10

eedrah


The no-cors mode limits the fetch API to operate like what is (and always has been) possible to with a form element, plus some "small" extra capabilities — to perform a type of requests which the MDN documentation calls "simple". Additionally, just like with forms, the response is not accessible.

Forms (and all other HTML elements) can do cross-site requests without using the CORS protocol — their existence and behavior predates the CORS protocol.

The extra capabilities are those of simple requests which are beyond what HTML elements can achieve: using the HEAD method, changing the value of simple headers, set/change parameters of simple content types, such as charset.

I believe that the no-cors mode exists to support the implementation of requests made by HTML elements. This statement from the MDN documentation for mode seems to corroborate this idea:

However, for requests created other than by the Request() constructor, no-cors is typically used as the mode; for example, for embedded resources where the request is initiated from markup, unless the crossorigin attribute is present, the request is in most cases made using the no-cors mode — that is, for the <link> or <script> elements (except when used with modules), or <img>, <audio>, <video>, <object>, <embed>, or <iframe> elements.

Apart from that, all cross-site use cases I can think of could instead use the cors mode, offering more functionality (complex requests), without loss of safety.

A note regarding request simplicity/complexity. In the fetch/CORS specification the same request type concept is called CORS "safe", where safety refers to safe for old servers in the face of the new "unsafe" request types introduced by XHR/fetch. Simple requests themselves can, however, in no way be considered generally safe. In fact, it is these same request types which, when causing server-side persistent state mutations, must be protected against CSRF attacks! I guess this is why MDN rebaptized them as "simple" (yet generally unsafe!). For reference, the fetch/CORS specification mentions CORS-safety regarding methods, headers, the latter including the special Content-Type header, among others.

like image 23
Duarte Cunha Leão Avatar answered Oct 13 '22 22:10

Duarte Cunha Leão