Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why are cross origin workers blocked and why is the workaround ok?

Tags:

Recently I worked on a library that supports using workers for some heavy lifting.

I found out that, at least on most online code editors (snippets/jsfiddle/codepen/glitch) I can't seem to load a worker from another domain. I get a security error (or in firefox silent failure)

function startWorker(url) {
  try {
    const worker = new Worker(url);
    console.log('started worker');
    worker.onmessage = e => log('black', e.data);
    worker.postMessage('Hi from page');
  } catch (e) {
    console.error('could not start worker:', e);
  }
}

const workerURL = 'https://greggman.github.io/doodles/test/ping-worker.js';
startWorker(workerURL);

In Chrome and Safari I get

SecurityError: Failed to construct 'Worker': Script at 'https://greggman.github.io/doodles/test/ping-worker.js' cannot be accessed from origin 'https://...'.

Question #1: Why do I get that error?

What setting causes it? iframe options? http headers for the page? http headers for the iframe? http headers from the script?)

Question #2: Is there a way to detect this issue in firefox?

I can send a message from the worker and timeout but I'm wondering if there is some less indirect way of checking for success/failure

In any case I can work around this issue by fetching the text of the script myself

function startWorker(url) {
  try {
    const worker = new Worker(url);
    console.log('started worker');
    worker.onmessage = e => console.log(e.data);
    worker.postMessage('Hi from page');
  } catch (e) {
    console.error('could not start worker:', e);
  }
}

async function main() {
  const workerURL = 'https://greggman.github.io/doodles/test/ping-worker.js';

  const res = await fetch(workerURL);
  const text = await res.text();
  const blob = new Blob([text], {type: 'application/javascript'});
  const url = URL.createObjectURL(blob);

  startWorker(url);
}
main();

I asked the browser teams and was told fetching manually and making a blob url is okay which leads to my main question.

Question #3: What's the point of this security error given the workaround is trival?

Given there is a workaround in what situation is there no workaround? What is the point of the Security Error? The browser vendors say my workaround is fine and I've actually be using the ability launch things as blob urls for 7-8 years. (html, scripts, but not workers until now) but if my workaround is fine then what is the point of the error?

like image 696
gman Avatar asked Sep 25 '19 12:09

gman


People also ask

How do you fix cross-origin request blocked the same-origin policy disallows reading the remote resource at?

Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource. This can be fixed by moving the resource to the same domain or enabling CORS.

What is the purpose of CORS?

Cross-Origin Resource Sharing (CORS) is an HTTP-header based mechanism that allows a server to indicate any origins (domain, scheme, or port) other than its own from which a browser should permit loading resources.

What is same-origin policy and CORS?

The same-origin policy is an important security feature of any modern browser. Its purpose is to restrict cross-origin interactions between documents, scripts, or media files from one origin to a web page with a different origin. The HTTP protocol was extremely simple when it was first created.


1 Answers

Question #1: Why do I get that error?

Because that's what the specs ask. From fetch a classic worker

  1. Let request be a new request whose url is url, client is fetch client settings object, destination is destination, mode is "same-origin", credentials mode is "same-origin", parser metadata is "not parser-inserted", and whose use-URL-credentials flag is set.

So the request will have its mode set to "same-origin", and because of that, it will fail:

(async ()=>{
const url = "https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js";
try {
  console.log( 'default ("no-cors")' )
await fetch( url )
  console.log( 'success' );
}
catch(e) { console.log( 'failed' ); }

try {
  console.log( 'like workers ("same-origin")' )
await fetch( url, { mode: "same-origin" } )
  console.log( 'success' );
}
catch(e) { console.log( 'failed' ); }

})();

Question #2: Is there a way to detect this issue in firefox?

Sure, you just have to listen for the error event that will be dispatched on your Worker object:

const url = "https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"
const worker = new Worker( url );
worker.onerror = console.error;

Question #3: What's the point of this security error given the workaround is trival?

Because the internal origin of your Worker depends on this. ref

Set worker global scope's url to response's url.

So if they were to allow a "no-cors" request here, you would be able to fetch resources on that server from your Worker, bypassing the cross-origin restrictions.

By fetching it first, and then creating a same-origin (blob:URI) or an opaque origin (data:URI) context, there is no such problem.


Note that only the initial request to the Worker's script is subject to this limitation, so an other way to work around your initial issue is to use the importScripts method from inside a "same-origin" Worker:

const worker = new Worker( getURL() );
worker.onmessage = (evt) => console.log(evt.data);


function getURL() {
  const txt = document.getElementById( 'source' ).textContent;
  return URL.createObjectURL( new Blob( [ txt ] ) );
}
<script id="source" type="worker">
  importScripts("https://greggman.github.io/doodles/test/ping-worker.js");
</script>
like image 156
Kaiido Avatar answered Oct 18 '22 19:10

Kaiido