What is blob: & why avoid it in CSP ?
blob: scheme in Content Security Policy allows resources to be loaded from URLs created by the Blob API, typically in-memory or temporary URLs that JavaScript creates at runtime. Allowing blob: in script or style directives can undermine CSP by letting the page execute or apply dynamically constructed code. This article explains the risks and how to remove or restrict blob: to mitigate them.
What Is the blob: Scheme?
A blob URL (e.g.blob:https://example.com/uuid) is a URL that refers to a Blob or File object in the browser. Scripts can create such URLs and use them in <script src="...">, <link href="...">, workers, or other APIs. CSP can allow or block these via source lists such as script-src 'self' blob:.
A classic use of blob: URLs — enabling users to download in-memory generated files without any server round-trip.
Content-Security-Policy: script-src 'self' blob:;
<!-- Example: Create a blob URL for downloading a text file -->
<script>
// Prepare some text to download as a file
const textData = "Hello, world! This file was generated in the browser.";
const blob = new Blob([textData], { type: "text/plain" });
const blobUrl = URL.createObjectURL(blob);
// Create a link for download
const a = document.createElement('a');
a.href = blobUrl;
a.download = "hello.txt";
a.textContent = "Download file";
document.body.appendChild(a);
</script>
Classic use: Calculating the sum of numbers in a Web Worker loaded via a blob: URL — demonstrates functional, legitimate use of blob for background tasks.
Content-Security-Policy: script-src 'self' blob:;
<!-- Classic example: using a blob: URL to run a Web Worker performing a calculation -->
<script>
// Worker code that sums an array sent from the main thread
const workerCode = `
self.onmessage = function(event) {
// Receive array, sum it
const numbers = event.data;
const sum = numbers.reduce((acc, value) => acc + value, 0);
// Send result back
postMessage(sum);
};
`;
// Create a blob and a blob URL
const workerBlob = new Blob([workerCode], { type: "application/javascript" });
const workerBlobUrl = URL.createObjectURL(workerBlob);
// Create the worker from blob: URL
const worker = new Worker(workerBlobUrl);
// Send array to worker; receive the sum
worker.onmessage = function(event) {
alert("Sum calculated by the worker: " + event.data); // e.g., alerts "Sum calculated by the worker: 15"
}
worker.postMessage([1, 2, 3, 4, 5]);
</script>
Common Use Cases
Common use cases of blob URLs include:- Media : Playing audio or video from in-memory or recorded data (e.g. media-src).
- Downloads : Generating files for the user to download (often not script/style).
- Workers : Loading a worker script from a blob URL (e.g. worker-src or child-src).
blob: becomes dangerous when it can execute arbitrary code. Any script on the page can create a blob containing arbitrary JavaScript or CSS and load it via a blob URL. That is effectively dynamic code execution, similar to eval or inline scripts, and can defeat the purpose of CSP.
Security Vulnerabilities
Whenblob: is allowed in script-src (or style-src):
- Bypass of script/style restrictions : If an attacker can run script (e.g. via XSS or a compromised dependency), they can build a blob with malicious content, create a blob URL, and load it in a
<script>or<link>. CSP would allow it because the source isblob:, not because the content was approved. - No integrity or allowlist : Blob URLs are opaque and short-lived. You cannot allowlist them by host or hash. Allowing
blob:in script-src or style-src is a broad permission for any script on the page to load arbitrary code. - Harder to audit : Unlike static files or inline blocks, blob content is generated at runtime, so it's difficult to review or correlate with violation reports.
How allowing blob: in script-src lets runtime scripts run arbitrary code by creating script tags loading blob: URLs.
Content-Security-Policy: script-src 'self' blob:;
<!-- Dangerous: any script on the page can create and execute a blob: URL -->
<script>
// Create a malicious JS payload
const code = "alert('Blob XSS: ' + document.domain)";
const blob = new Blob([code], { type: "text/javascript" });
const blobUrl = URL.createObjectURL(blob);
// Dynamically load the payload as a script tag
const s = document.createElement('script');
s.src = blobUrl;
document.body.appendChild(s);
</script>
How allowing blob: in script-src enables runtime creation and execution of malicious workers via blob URLs.
Content-Security-Policy: script-src 'self' blob:;
<!-- Example: Blob URL used in a Worker (can execute attacker JavaScript) -->
<script>
// Malicious code for the worker
const workerCode = "postMessage('XSS from worker: ' + self.origin)";
const workerBlob = new Blob([workerCode], { type: "application/javascript" });
const workerBlobUrl = URL.createObjectURL(workerBlob);
// Instantiating the Worker from a blob: URL
const w = new Worker(workerBlobUrl);
w.onmessage = function(event) {
// Show the message from the malicious worker
alert(event.data);
};
</script>
How to Remove or Restrict blob:
- Move scripts and styles off blob: URLs : Serve scripts and styles from your origin or a trusted CDN. Use nonces or hashes in CSP to allow only the scripts you intend. For workers, prefer serving worker scripts from static URLs (e.g.
/workers/task.js) instead of creating them from blobs at runtime. - Tighten directives : Remove
blob:from the sensible directives of the CSP.If you must use it, keep it out of any directive that affects script or style execution. - Test with report-only : Use Content-Security-Policy-Report-Only and reporting to see what would break. Fix legitimate uses by serving resources from proper URLs or static worker scripts, then switch to enforce mode without
blob:in script or style directives.
How to safely load scripts, styles, and workers without using blob: in sensible directives.
Content-Security-Policy: script-src 'self'; style-src 'self'; worker-src 'self'; media-src 'self' blob:;
<!-- Scripts and styles from your own origin -->
<script src="/static/js/app.js"></script>
<!-- Worker loaded from a static URL instead of a blob: -->
<script>
const worker = new Worker('/static/workers/task.js');
worker.postMessage({ data: [1, 2, 3, 4, 5] });
</script>blob: use.
See also
- Scheme source — How CSP treats blob:, data:, and other schemes
- The data: scheme in CSP — Similar risks with data URIs
- worker-src — Controlling worker scripts without over-allowing blob:
Continue Reading
CSP enforce & report only
Understanding the difference between enforce and report only modes in Content-Security-Policy implementation.
How to use the Content-Security-Policy (CSP) Builder
Learn how to use the Content-Security-Policy (CSP) Builder to generate a policy based on actual usage patterns.