Content Security Policy worker-src

Krishna Chirumamilla
3 min readNov 12, 2019

--

Worker-src is a Content Security Policy (CSP) Level 3 directive that was introduced to specify valid sources for worker scripts (worker, shared worker and service worker)

Web Workers makes it possible to run a script operation in a background thread separate from the main execution thread of a web application.

The main purpose of this directive is to prevent your already loaded scripts from loading more scripts in the form of workers. Even though workers don’t have access to the DOM of the calling page and are loaded in a different execution context, they can still be dangerous. An example would be where a malicious Web Workers can use excessive CPU for computation, leading to DoS. Hence, a separate directive for workers was necessary as workers can be dangerous.

When you want to allow a third-party to run scripts on your page, you punch a hole in your CSP and say

Content-Security-Policy: script-src *.3rdPartyJS.com

With this, the browser allows js from *.3rdPartyJS.com to run on your page. Now what happens if *.3rdPartyJS.com loads a worker? When loading such a worker the browser should usually look for worker-src directive in CSP. If worker-src is not present, then browser should fall back to script-src to see if you have specified something there. Let’s see how different browsers behave with minor changes to CSP.

To understand how browsers behave, I have considered a simple use case where I would like to load a 3rd party js, which further loads a worker. For this, I have modified the existing repo from MDN on dedicated workers.

In my case, I am pulling main.js file that is hosted in a remote location. main.js file tries to pull the worker.js file, which is also hosted in a remote location. Repo link here.

Browsers don’t allow you to create a worker with a URL pointing to a different domain. That means you can’t just do this:

const myWorker = new Worker(“https://www.3rdPartyJS.com/worker.js");

If you do that in main.js file, firefox doesn’t load worker.js. But it doesn’t throw an error. Whereas Chrome on the other hand throws a console error.

Uncaught DOMException: Failed to construct ‘Worker’: Script at ‘https://www.3rdPartyJS.com/worker.js' cannot be accessed from origin ‘http://localhost:3000'.

Instead, 3rdPartyJS.com should load this by creating a blob URL, which can be used to initialize the worker.

const myWorker = function createWorker (workerUrl) {var worker = null;var blob;blob = new Blob(["importScripts('" + workerUrl + "');"], { "type": 'application/javascript' });var url = window.URL || window.webkitURL;var blobUrl = url.createObjectURL(blob);worker = new Worker(blobUrl);return worker;};const newWorker = myWorker("https://www.3rdPartyJS.com/worker.js")

With the correct method to load the worker figured out, let’s see how browsers behave with the CSP.

When there’s no CSP set, browsers load both the 3rd party js and it’s worker just fine.

But when the CSP is set to this Content-Security-Policy: script-src *.3rdPartyJS.com;, you are going to get an error from browsers like this:

Firefox:

Content Security Policy: The page’s settings blocked the loading of a resource at blob:http://localhost:3000/b36b8228-1d8f-fc48-9ec5-cc12ce0d7dee (“script-src”).

Chrome:

Refused to create a worker from ‘blob:http://localhost:3000/81d6835f-c9a5-4df0-9d39-bff15569a572' because it violates the following Content Security Policy directive: “script-src *.3rdPartyJS.com”. Note that ‘worker-src’ was not explicitly set, so ‘script-src’ is used as a fallback.

What’s interesting is Safari loads the worker without throwing an error.

But both Chrome and Firefox load the worker when any of the following two CSPs are used.

Content-Security-Policy: script-src *.netlify.com; child-src blob:

or

Content-Security-Policy: script-src *.netlify.com; worker-src blob:

worker-src is not supported in Safari yet. When child-src is used, Firefox throws a console warning child-src has been deprecated.

It’s important to note that worker-src is not yet supported in Safari and IE. Hence, it’s always a good idea to have child-src in your CSP. If these are not present, ideally browsers should fall back to script-src or default-src. From security perspective, as long as you have a good CSP that only allows loading the JS from the sources you know, you should be good to load the workers from these sources. Because a good CSP only allows loading the JS from the sources you know and only these sources can load the workers. With a good CSP in place, if you add in Subresource Integrity to these Javascripts, it’s a really good security practice.

--

--