Mir-Ismaili
Mir-Ismaili

Reputation: 17184

Security issue: CacheStorage can violate Content Security Policy (CSP) restrictions

Overview

You know with this CSP configuration:

Content-Security-Policy: script-src 'self'

the following code can't be executed:

// Content-Security-Policy: script-src 'self'
const dangerousStringToBeEvaluated= "console.log('HACKED!')"
eval(dangerousStringToBeEvaluated) // EvalError: Refused to evaluate a string as JavaScript because 'unsafe-eval' is not an allowed source of script in the following Content Security Policy directive: "script-src 'self'

This ensures that eval and similar operations are blocked unless explicitly allowed by unsafe-eval.

Exploiting CacheStorage to Bypass CSP

However, if a malicious actor knows that a specific JavaScript file is cached in CacheStorage and served via a service-worker, they can bypass this restriction by modifying the cached content. Here's how it works:

// Content-Security-Policy: script-src 'self'
const dangerousStringToBeEvaluated = "console.log('HACKED!')"
const cache = await caches.open('some-cache')
await cache.put(
  '/some-file.js',
  new Response(
    dangerousStringToBeEvaluated, // --> Go to execute on the subsequent `import`!
    {headers: {'Content-Type': 'application/javascript'}},
  ),
)

Now, when /some-file.js is imported, you'll see HACKED! logged in the console!

Question

Is there a CSP directive to prevent this behavior? For example, can CSP limit write access to CacheStorage to ensure the integrity of cached content?

Context

CSP is enforced on all my files for all types of scripts (script-src 'self'; worker-src 'self'; script-src-elem 'self';), including the service-worker itself and all files that are cached by it. So for example, if you set dangerousStringToBeEvaluated to:

const dangerousStringToBeEvaluated = "console.log('HACKED!'); eval(`console.log('HACKED MORE!')`)"

Then the first log works, but the second one inside eval is blocked.

Upvotes: 0

Views: 66

Answers (1)

Halvor Sakshaug
Halvor Sakshaug

Reputation: 3475

I wasn't able to reproduce, but workers fall under "worker-src" directive. It has a fallback to "script-src", but I think it only controls hosts and schemes. Can't find a definitive answer in the specification. To block, set "worker-src 'none'".

Upvotes: 0

Related Questions