Content Security Policy Bypass

Это изображение имеет пустой атрибут alt; его имя файла - content-security-policy-1-1024x414.png

In Russian:

Content Security Policy (CSP) is an additional security mechanism built into browsers to prevent Cross Site Scripting (XSS).

CSP allows to define whitelists of sources for JavaScript, CSS, images, frames, XHR connections. Also, CSP can limit inline script execution, loading a current page in a frame, etc.

Let’s take a look at different CSP configurations and their weaknesses.

In some cases, the CSP allows the execution of inline scripts (the unsafe-inline directive), and the Content-Security-Policy header is not returned on all pages (it’s set by the backend or via the meta tag, rather than the webserver). In this case you can open a page for which no policy has been set and overwrite its contents (see In this case, if the frame-src directive restricts the creation of frames, you will have to create a window through

We’ll consider XSS exploitation scenarios when the above conditions are not met, and thus the method does not work.

Exploitation comes down to two steps: first, you need to learn how to execute arbitrary code, and then – to obtain the necessary data from the page in the victim’s browser.

Code Execution

We’ll consider bypasses depending on the presence of unsafe-inline and unsafe-eval directives.

Unsafe inline enabled

Example configuration:

Content-Security-Policy: default-src 'self'; script-src 'self' 'unsafe-inline';

Inline execution

With inline execution enabled we can simply injection our code into the page.

Google Tag Manager

Sometimes you need to collect a lot of information by running a fairly large script. The whole code may not fit into a payload and in such a case, it must be dynamically loaded from some origin.

In our case you can look at the policy and see that the scripts from are allowed to run. In this Google’s service you can save a custom HTML, in which, fortunately, you can insert your JS-code. Please note that the ECMAScript6 specification is not supported by default.

You can insert your code by adding tags. For example, let’s try to add code with an alert and embed it on a site with a CSP policy configured.

To execute the code from the Tag Manager, you can insert the block:

You can shorten the code different ways, such as:

Only the id changes here, you can see it in your personal Tag Manager account.

The point is to include the gtm.js script and add the ‘gtm.js’ event to the queue, which will trigger the payload.

Unsafe eval enabled

Example configuration:

Content-Security-Policy: default-src 'self'; script-src 'self' 'unsafe-eval';


With unsafe-eval policy enabled we can perform a Client-Side Template Injection attack.

If a template library (such as Vue.JS, Angular, JQuery, etc.) is not connected to the page, we need to connect it. The host is good for this, which is often allowed in CSP to import some libraries needed for the site.

For example, let’s connect AngularJS of the old version (1.4.6) and execute arbitrary code through template implementation.

Final payload:

In this payload we create a div element with the ng-app attribute that activates AngularJS, and in the div we insert a template that exploits CSTI in AngularJS 1.4.6, the result is the execution of alert(1).

DOM-based XSS

In addition to CSTI, any other type of DOM-based XSS, in which the content of some HTML element falls into the eval() call, can help to execute arbitrary code if unsafe-eval is present.

Unsafe inline and eval disabled

Example configuration:

Content-Security-Policy: default-src 'self'; script-src 'self'  * * *;

File Hosting

Sometimes script-src might include domains like with a lot of services, such as Yandex.Disk, You can upload the payload to the Yandex.Disk storage, copy the download link and replace the content_type parameter value in the link with application/javascript, so that the server will return the appropriate header and the browser will allow script execution:

Note that the link to the file is temporary, and after some time (about 4 hours) it stops working.

If script-src directive includes * and *, the script can be imported from Google Drive in a similar way:

Both origins are required to be whitelisted due to the temporary Cookie Identifier, which is created before being redirected to *

File upload or JS/JSON/JSONP injections

It is worth checking the upload functionality of the application, perhaps it allows you to upload JS-files, then the script can be imported from the same domain.

JSONP hijacking or unexploitable injection of content into JavaScript or JSON and can also help. For example, if there is no function name validation in JSONP, you can replace the called function with arbitrary JS code.

Leaking Data

Suppose we’ve achieved arbitrary code execution on a page. Now the task is to extract the data and transfer it to the host we control.

Connections allowed

Example configuration:

Content-Security-Policy: default-src 'self'; connect-src: 'self' * *;

Analytics Systems

If the policy allows the Google Analytics host, we can send data there. Please note that the maximum amount of data accepted per request is 8KB.

The link to send the data looks like this:***&cid=123&t=event&ec=email&el=321&cs=newsletter&cm=email&cn=&cm1=1&ea=data

After that, you will see in the events the data that you sent in the ea parameter:

Это изображение имеет пустой атрибут alt; его имя файла - VdSz49yptmAxqfSPRQ10pBXt-7Xs40cptljRECqZxt_BJPAG9UZ-SSJyHZ2uKF7ECZsv_0Vr1rBTGaFQQMu7OKvFYkPZ_vNAzXbBkpW9f_HfGjF1RBSzG2y_h_0LnLTKvkblDtFT

Surveys, Chats, etc

If the site uses customer support chat systems, you can try to use them to send data.

If you have *.google or * in connect-src, you can use services that are located on these subdomains, namely Google Forms and Yandex Surveys.

An example of sending data to Google Forms –

It’s pretty simple, just a single POST request:

An example of sending data to Yandex Surveys –

For the Yandex Surveys you will need to generate a unique value for ivid, the sending function will look like this:

Connections not allowed

Example configuration:

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


If you are not allowed to connect to any external host, you can send data directly in the URL (query string) by redirecting the user to your web server:

Sending Internally

If the application allows you to leave comments, write messages, you can use this functionality to transfer data within the application. It is sufficient to register an account that will receive the stolen information in a personal message or comment.

What should we do?

Okay, we’ve learned how to break CSP, but the more important question is how to secure the application.

Be restrictive

CSP has a “if not forbidden then it’s allowed” rule, and you should be more careful about the origins that the user’s browser will be allowed to access. Often it is easier for developers to allow extra domains than to know what specific host is needed for the frontend. Ideally, all necessary scripts should be placed on your hosts, and there should be nothing else allowed by the CSP.

You can visit to check your CSP for some common weaknesses. This service will analyze the configuration and explain possible threats.

Это изображение имеет пустой атрибут alt; его имя файла - PBR08Jzq2CSxXibiRxmAv1nsFaHC_7TPqZRVLnrm-3cqCcpoEJ7BjvaHlvzlT1rjfunWP_J-QmyVpKNbTkTOpRq4P1ZEIxrDy7xeDNiflVQ4-6risPdzU12SlCJY3JVqW_trpTWV

Use report-uri

Or report-to, which will force the browser to report CSP errors (including unsuccessful attempts to exploit XSS). This will help you to debug CSP properly and also quickly detect the attacks.

Disable unsafe-inline

Alternatively use nonce, and also do not allow unsafe-eval.

Watch for XSS!

The CSP mechanism is only a mitigation measure that makes XSS difficult to exploit but often does not make it impossible.

Perform regular pentests

To verify that your CSP is configured correctly and your application is not vulnerable to XSS and other attacks, please contact us for a web application penetration testing.

Leave a Reply

Your email address will not be published. Required fields are marked *