top of page

Defending Against XSS Using CSP (script-src)

  • Writer: Shounak Itraj
    Shounak Itraj
  • May 23, 2020
  • 6 min read

In previous blog, we have seen about setting up CSP policy is majorly used for three types of attacks:XSS: The ability to inject and execute untrusted scripts in a vulnerable applications (This is protected with direcitves like script-src, object-src, default-src) Clickjacking: Force user to take unwanted actions in your application by overlaying hidden frames on attacker controlled pages. (This is protected with frame-src, child-src) Mixed Content Resource Loading: Sometimes the website which is running on HTTPS protocol loads content through insecure HTTP protocol. (This is protected using the directives, upgrade-insecure-requests, block-all-mixed-content)

In this blog, we will have two parts, in first part we will see the valid sources of script-src directive and in second part we will look at defending against XSS by setting up correct CSP policy and also examples of misconfiguration of CSP policy which defies the whole purpose of using CSP for XSS defense.

script-src

The Content-Security-Policy‘s script-src directive is used to control the valid sources of javascript. This includes URLs which are loaded directly in tag, javascript event handlers like (onclick).

Syntax

Content-Security-Policy: script-src 
Content-Security-Policy: script-src  

The can be of the following: In simple words, this source accepts the hostname or IP address of the domain which needs to be added in whitelist in CSP policy for loading resources from. The source contains site address and optional wildcard (*) characters either in URL scheme (http*) or at the begining of site (to whitelist subdomains) or for ports (to allow all ports of the given domain) Examples: http://*.yourwebapp.com allows all attempts to load from any subdomain of yourwebapp.com over nonsecure http URL scheme. contact.yourwebapp.com:443 allows all attempts to load resources from contact.yourwebapp.com on port 443 only. *://yourwebapp.com allows all attempts to load resources from yourwebapp.com on any scheme. (e.g. http or https)

This source accepts scheme sources like http:, https: (colon is required in policy) Example Content-Security-Policy: script-src http: This will to load resources from any source over http Content-Security-Policy: script-src http: https: This will to load resources from any source over http and https There are also other data schemes, but these are not recommended to use: data: Allows data: URIs to be used as content source. This is insecure, an attacker can inject arbitrary data: mediastream: Allows mediastream: URIs as content source. blob: Allows blob: URIs as content source filesystem: Allows filesystem: URI as content source.

'self' This source is used when you want to load the resources from the same origin (scheme, host and port) on which your website is hosted. Some browsers specifically exclude blob and filesystem from source directives. Sites needs to allow these content types can specify them using the Data attribute.

'unsafe-eval' Allows the use of eval() and similar methods for creating code from strings.

'unsafe-hashes' Allows to enable specific inline event-handlers. If only inline event handlers are needed to allow but not inline and javascript: URLs scheme. This is safe to use than using 'unsafe-inline'.

'unsafe-inline' Allows inline tag, javascript: URLs, inline event handlers and inline elements. This source expression in particular is considered to be most insecure to use in CSP. Using this expression is as good as not using CSP against XSS attack.

'none' Blocks every attempt of loading script from any source.

'nonce ' A whitelist for specific inline scripts using cryptographic random nonce which should be used one time only. It is critical to use unguessable nonce, so it becomes difficult to bypass the CSP policy using nonce. Modern browser will ignore 'unsafe-inline' if nonce is used in CSP. Example Content-Security-Policy: script-src 'nonce-MTIzNA=='


 var newVar = 1;

'-' a sha356, sha384 and sha512 hash values of inline scripts, styles. If it is inevitable to use inline scripts and styles then it is recommended to either use nonce or hashes for making script-src effective against XSS. But the hashes can only be used if the contents of scripts are static. Example Content-Security-Policy: script-src 'sha256-ta0gKqVFGfIlXlnRgI1kEBx4g7ZNJM2wlbw68nteuYk='


 var newVar = 1;

'strict-dynamic' If this source is used in script-src directive, it allows dynamically added scripts. This menas that scripts created by document.createElement('script') will be allowed by the policy, regardless of whether the URL from which they are loaded is in the script-src whitelist.

If this source is defined then host-source, scheme-source, 'unsafe-inline', 'self' will be ignored when loading script. Scripts request which are triggered by non parser-inserted are allowed.

We will use following example to explain this: Content-Security-Policy: script-src 'nonce-MTIxd2ZzZGYyMzIxM3dmd2FmMTIx' 'strict-dynamic' The HTML page looks like below:

...

...

This will include script.js and it will not be blocked as nonce in script tag matches with the one configured in CSP Policy. The script.js looks like below:

var str = document.createElement('script');
str.src = "js/getAllElements.js";
document.head.appendChild(str);

document.write('');

Now getAllElements.js will get loaded because the element got created using document.createElement() but listAll.js will get blocked as the element is created using document.write which is parser-inserted.

You can test this behavior Here. Please note that getElements.js file will alert(1) when you open the URL. listAll.js contains alert(2) (Which will be blocked as shown in below screenshot.)

CSP with nonce and ‘strict-dynamic’

‘report-sample’ Report the incidence when the policy is violated by any user.

 

Browser Compatibility

.tg {border-collapse:collapse;border-spacing:0;border-color:#9ABAD9; table-layout: auto;} .tg td{font-family:Arial, sans-serif;font-size:14px;padding:10px 5px;border-style:solid;border-width:1px;overflow:hidden;word-break:normal;border-color:#9ABAD9;color:#444;background-color:#FFFFFF;} .tg th{font-family:Arial, sans-serif;font-size:14px;font-weight:normal;padding:10px 5px;border-style:solid;border-width:1px;overflow:hidden;word-break:normal;border-color:#9ABAD9;color:#000;background-color:#EBF5FF;} .tg .tg-cly1{text-align:center;vertical-align:middle} .tg .tg-0lax{text-align:left;vertical-align:top} .tab {position:absolute;left:150px;}

BrowsersChromeEdgeFirefoxInternet ExploereOperaSafariAndroid WebviewChrome for AndroidFirefox for AndroidSafari on iOSSamsung Internet

script-srcYesYesYesNoYesYesYesYesYesYesYes

Now that we have seen script-src directive and its possible sources, the object-src directive has same resources and browser support as script-src just that object-src does not have 'strict-dynamic' source type.

 

Configuring CSP Against XSS

In this second part of this blog, we will cover the more robust way of configuring CSP against XSS. Primay purpose of CSP is protection against XSS. CSP works on whitelisting of origin of resources to be loaded on webpage.

On the otherhand, CSP also offers more granular methos of granting trusts to scripts through: crypthographic nonce, hashes, 'strict-dynamic'. Cryptographic nonces allow developers to identify whitelisted sources on the given webpage and allow only those scripts to execute, while hashes allow developers to execute static scripts on their webpages.

Sometimes it is difficult to use only nonce-based policy as it is required to whitelist few of the hosts for script source. In these situatusions CSP allows to set globally both nonce-based policy and whitelist of hosts. In such cases browser excutes nonce-based scripts and also allows to load scripts from whitelisted hosts. Following example is of whitelist and nonce-based policy

Content-Security Policy: script-src https://cdn.example.com default-src https://example2.com  script-src 'nonce-random'

As shown in following screenshots, the difference in responses when used nonce and whitelist domains in combination. The first screenshot shows that jquery is blocked while loading as whitelisting is removed, but script with nonce gets executed because it is present in CSP policy. You can check this behavior Here

JQuery gets blocked because ajax.googleapis.com is not whitelisted

The second screenshot shows that the jquery is successfully loaded because domain is whitelisted through CSP and also script with nonce 1234 got executed to show alert window. You can check this behavior Here

JQuery is allowed to load as ajax.googleapis.com is whitelisted

Now going ahead with this, it is recommended to avoid the whitelisting of domains if possible. But few libraries include more javascripts within them and the libraries are not aware of the CSP policies, for example if only nonce-based approach is are implemented. In these cases dynamically inserted scripts would be blocked from executing by CSP, and parts of the application would not work as expected.

To overcome this problem and facilitate safe policies without relying on the source lists, there new source expression 'strict-dynamic' (which is of CSP3 specification) should be used. As explained above 'strict-dynamic' expression allows to execute elements which are created via non parser-inserted API i.e. document.createElement(‘script’).

In this way if nonce-based policy is combined with 'strict-dynamic' expression then the policy will be more robust against XSS attacks. Because if attacker finds any injection point in Markup language, he/she will not know the exact nonce to include in script element. Also document.createElement() API has to be executed in Javascript which again will not be possible without using either . or Javascript handler (

, )in any other elements. The nonce expression will block the Javascript event handler invoking and also . without correct nonce.

Thus using the combination of nonce and 'strict-dynamic' expression offers capability to execute scripts controlled by developer by setting nonces on trusted scripts and allowing trust to propogate to subscripts by setting 'strict-dynamic'.

Content-Security-Policy: script-src 'nonce-random13' 'strict-dynamic'

Limitations

As we are discussing about the advantages of 'strict-dynamic' here, there are few limitations with this approach:

If the injection point is in “src” attribute of dynamically created scripts with 'strict-dynamic' i.e. if the root cause of XSS bug is the injection of untrusted data into a URL passed to the src-attribute of a script, created using document.createElement() API, then the bug will become exploitable.

Also if the injection point is in nonce based then attacker will execute the payload. But in this scenario, even non nonce based approach is also vulnerable.

Conclusion

Considering above all examples and scenarios it is best to use nonce-based policy along with ‘strict-dynamic’ (If required to load external scripts through JavaScript libraries) to mitigate majority of XSS attacks.

Recent Posts

See All
Port Scanner using Go Programming

Being in security industry we regularly need to write some simple scripts for automating few of our tasks like calling some APIs, parsing...

 
 
 

コメント


bottom of page