When building a web app with separated frontend and backend (no server side rendering) I still want to make use of CSP nonce. Usually the CSP header and the html should contain the same nonce, which is no problem with SSR but seems to be impossible without SSR.
Now I thought of a different way to make use of the nonce without SSR:
Server: Besides sending the nonce with the CSP header, also send it as a cookie (still changing for every request!) This also seems to be a common approach for CSRF
UI: instead of including the <script> which needs a nonce directly inside the html, rather load it dynamically from my own script:
const script = document.createElement('script')
script.setAttribute('src', 'https://example.com')
script.setAttribute('data-csp-nonce', getCspNonceFromCookie())
document.head.appendChild(script);
Is this a valid approach for this problem? Are there security concerns?
Your solution works. One of my projects does it in a similar way. But you should set a nonce attribute instead of data-csp-nonce.
script.setAttribute('nonce', 'THE-GENERATED-NONCE')
So the script tag looks like:
<script nonce="jETnT70lr0T3Hw4b5WeCjuJ421a3kcBl">
// ...
</script>
This is only secure when the headers, send by the server, includes already content-security-policy which denies external scripts. E.g.:
content-security-policy: default-src 'self'; script-src 'self' 'nonce-jETnT70lr0T3Hw4b5WeCjuJ421a3kcBl'; img-src 'self' data:; object-src 'none'
Related
I am trying to load jQuery in Electron (v. 16.0.0), but I get this error:
Inside the head element I have included this line:
<meta http-equiv="Content-Security-Policy" content="script-src 'self';">
Also, inside the body element, I am trying to load jQuery like this:
<script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script>
I have tried so many ways to find a solution for this, but to no avail. Previously, I also tried to load jQuery like this, but it gave me a similar error, shown below:
<script>window.$ = window.jQuery = require('./libraries/jQuery/jquery.min.js');</script>
Answers to a related question did not work for me either. What should I do?
The reason Electron, or any other Web browser that implements Content Security Policy, for that matter, would correctly refuse to load a script from an arbitrary origin (URL), or even an "inline" script (e.g. script text inside a script element), is because your security policy is explicitly specified to deny such attempts, with that meta element you said you added:
<meta http-equiv="Content-Security-Policy" content="script-src 'self';">
Why did you add it? Was it there by someone else's hand? Why is it there? It's the reason why Electron denies loading of the scripts in question.
The value of the content attribute above has the effect of instructing Electron to only allow loading scripts from the same origin as the origin of the document containing the meta element. That effectively excludes every other origin like https://code.jquery.com and inline scripts (which have to be allowed explicitly in this case because self denies these). Basically, the value is to be interpreted as "only allow loading scripts from the same site". Inline scripts are not considered as "same site".
Simpler put, you yourself prohibit loading of scripts from the kind of locations you then attempt to use, with that meta element.
You need to learn how Content Security Policy mechanism works and applies in your case. You will have to decide whether you want to allow loading of scripts from domains like code.jquery.com, or whether, for example, you will only want to allow loading scripts from your website, which in turn will probably necessitate you copying the JQuery library you want to use to be served by your website. You also will have to decide if you want to allow "inline" scripts on your site, for whatever reason you may consider necessary.
The security policy mechanism itself is very useful, don't shy away from it, it's there for a reason -- to help you prevent abuse of your site users by malicious scripts loaded by other malicious scripts or mechanisms. But you need to use it correctly, obviously.
You have 2 issues because of jQuery:
script-src 'self' does not allow to load script from https://code.jquery.com/jquery-3.6.0.min.js, that's why you observe Refused to load the script 'https://code.jquery.com/jquery-3.6.0.min.js'... error.
You have to adjust your CSP at least as script-src 'self' https://code.jquery.com;.
After page loads, the jQuery pick up all scripts having $() and place them into one inline script in the <head> section. That's why you observe Refused to execute inline script ... error.
This inline script can be resolved with either 'unsafe-inline' or 'unsafe-eval' or 'nonce-value'(for jQuery > 3.4).
Allowing 'unsafe-inline' is a very harmful advice, since such CSP will not protect against XSS at all (https://youtu.be/zlH_bBQMgkc?t=717).
Also Electron does not have the technical ability to refresh the 'nonce' value.
Therefore, the most secure CSP you can do is:
script-src 'self' 'unsafe-eval' https://code.jquery.com;
or much better:
default-src 'self'; script-src 'self' 'unsafe-eval' https://code.jquery.com;
Note: Contrary to a common misconception, 'self' does not mean the Same Origin Policy, CSP interprets 'self' much more broadly.
I want to access a website using javascript. but I am getting the following error in the console.
Refused to connect to 'https://example.com' because it violates the following Content Security Policy directive: "default-src 'self'". Note that 'connect-src' was not explicitly set, so 'default-src' is used as a fallback.
I have added the following meta tag in the index.html,
meta http-equiv="Content-Security-Policy" content="connect-src 'self' http://example.com;" />
Isn't this the right way to add CSP? please help.
You already have one CSP published with the default-src 'self' policy. Quite possible it was done via HTTP header.
Check the Helmet middleware settings in case of NodeJS server, or Header set in the .htaccess file in case on Apache server. In case of Nginx it can be add_header in the config.
By adding <meta> tag you just publish a second CSP, which does not overrides the first one. 2 CSPs works subsequently - all sources should pass both CSP.
Figure out where CSP is publushed and add connect-src 'self' http://example.com; there.
There is probably a simple solution to this, but I can't figure it out.
I have a CSP defined like so:
"Content-Security-Policy: default-src * 'unsafe-inline' 'unsafe-eval' data: blob:; style-src 'unsafe-inline' 'self' cdnjs.cloudflare.com fonts.googleapis.com; script-src www.google-analytics.com cdnjs.cloudflare.com 'self' 'nonce-". $_SESSION['nonce'] . "'; img-src * 'self' data:;object-src 'self' sias.dev:8000; connect-src * 'unsafe-inline'; frame-src 'self' sias.dev:8000 www.google.com;"
The CSP needs to be tweaked a little but is partially working. I need a nonce because I have some legacy sites that have some inline scripts. The nonce is generated dynamically and changed on page loads. However, I ran into an issue where I'm loading some 'self' scripts on a page called via AJAX (not inline, but script src tags. That is probably bad practice, but I don't want to load those except for the page called via ajax.
The strange thing is that I don't have any inline scripts on that page, just script src tags like below, where the nonce is dynamic and matches that in the CSP policy.
<script nonce = "" src="/js/create_dicom/js/plupload.full.min.js"></script>
<script nonce = "" src="/js/create_dicom/js/jquery.ui.plupload.min.js"></script>
<script nonce = "" src="/js/create_dicom/js/main.js"></script>
<script nonce = "" src="/bower/pdfjs/src/pdf.js"></script>
<script nonce = "" src="/bower/pdfjs/src/pdf.worker.js"></script>
When I don't use a nonce, I get:
"Content Security Policy: The page’s settings blocked the loading of a resource at inline (“script-src”)."
When I do use a nonce they load fine and the page works. I thought that you only needed a nonce for inline scripts and not 'self' script. Should I be using strict-dynamic somehow ? I don't need a nonce in the main page for script-src tags. They load fine there ?
Also, the places that I need a nonce are mostly on pages that are loaded via AJAX. Is there are way to refactor things (i.e. putting them in .js files and loading the relevant .js that way). That is kind of a pain because I don't have a script loader and I'd want to load just the relevant js for files loaded via AJAX.
Thanks.
Console err: The page’s settings blocked the loading of a resource at INLINE says then you definitely have an inline script blocked not external, therefore it's nothing to do with 'self'. You can look al right console corner and see script_name:line_number:column where it happens.
In case of lock externall script, the err looks like: The page’s settings blocked the loading of a resource at HTTPS://EXAMPLE.COM/PATH/SCRIPT.JS.
Could be 3 kinds of inline scripts: <script>...<script>, <a href='javascript:void(0)' and <a onclick='evtHandler()'. Firefox does not distinguish these, Chrome is more verbose, so see how it looks this error in Chrome browser.
There are 2 options:
- there is NO error in Chrome - this is good, FF is known by false positive violations
- there is error in Chrome - you have an inline script and Chrome will say which exactly.
Tweak CSP via browser console log is bad practice. You use pdf.min.js, but this script contains a piece of code:
const n = document.createElement("script");
n.src = e;
n.onload = t;
n.onerror = function() {
r(new Error("Cannot load script at: " + n.src))
};
(document.head || document.documentElement).appendChild(n)
which means that some external scripts could be loaded on some conditions. Only God knows what yoy need to press to occur that and from where this script will be loaded.
Therefore a right way (if site have a visitors) is to use the report-uri directive and analyse a violation reports while about 2 weeks.
PS: Just curious - you specify: connect-src * 'unsafe-inline'; so you do have inline scripts somewhere? By the way, 'unsafe-inline' token is not used in the connect-src directive, it will be ignored.
I have a single page application (built in .net core MVC 2.2), where html section are loaded on the fly.
On main document, added CSP policy with a dynamically generated header looks like:
Content-Security-Policy: script-src 'self' 'nonce-I64vb811BxRNGV9Xf0pM'
Then comes a page section loaded via jquery ajax load function. page section contains a script (from src) with a nonce. Page section served with CSP header looks like
Content-Security-Policy: script-src 'self' 'nonce-ci5TQsyidT8x2jwBLRHS'
all good but browsers (chrome, safari blocking the script), with message:
jqueryscriptsbundle.js:formatted:43 Refused to execute inline script
because it violates the following
Content Security Policy directive: "script-src 'self' 'nonce-I64vb811BxRNGV9Xf0pM'
So browser is comparing nonce of script i.e. ci5TQsyidT8x2jwBLRHS with root/main page nonce value I64vb811BxRNGV9Xf0pM hence script blocked.
Is it correct behavior ? What can I do to make it work?
Your initial CSP, loaded with the page, provides a nonce. What you're trying to do is add another nonce, via Ajax, after the initial page load. CSP explicitly doesn't allow this; that's not a bug -- it's the entire point.
What you might do is allow a script-src of strict-dynamic. This allows you to authorize a script, and anything loaded by that script is also authorized. See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/script-src
Background
The idea of the Content Security Policy was to tell web-browsers what content to load from where. This means that attackers should not be able to inject their own code if, for example, 'unsafe-inline' was not explicitly allowed (which is not the best thing to do).
Google also released a CSP Evaluator, which is designed to find possible mistakes in your policy. With the default settings, the tool recommends using the 'strict-dynamic' policy for 'script-src'. The idea behind it is that you write a loader for whichever JavaScript sourcees you require and forbid everything else.
The Problem
What is considered the "correct" way to implement such a loader? Should the loader be written yourself (see below for example) or should a tool be used to create such a loader? (Please note that this question is not asking for a specific tool recommendation)
Example
var imported = document.createElement('script');
imported.src = '/path/to/imported/script';
document.head.appendChild(imported);
Context
My website currently has the following policy:
default-src 'none';
img-src 'self';
style-src 'self' https://stackpath.bootstrapcdn.com 'sha256-bviLPwiqrYk7TOtr5i2eb7I5exfGcGEvVuxmITyg//c=';
script-src https://use.fontawesome.com https://code.jquery.com https://cdnjs.cloudflare.com https://stackpath.bootstrapcdn.com;
base-uri 'none';
form-action 'none';
frame-ancestors 'none';
Google's tool suggested the following:
Host whitelists can frequently be bypassed. Consider using 'strict-dynamic' in combination with CSP nonces or hashes.
As such, I want to implement a loader to load these JS frameworks and I want to know how to best approach this issue.
An immediate answer is that as long as the script you're dynamically loading (/path/to/imported/script) is hosted in a domain that you've already whitelisted in script-src, you don't have to modify your CSP or change your loader -- everything will work as expected.
However, a broader problem is that your script-src whitelist includes domains that host Javascript which can be used by an attacker who finds a markup injection bug in your application to bypass your CSP. For example, https://cdnjs.cloudflare.com hosts Angular (https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.7.2/angular.min.js) which can be used by an attacker to convert an HTML injection into arbitrary script execution (here is a paper about this).
The suggestion in the CSP Evaluator tool is to switch your application to rely on a script-src which uses CSP nonces instead of the whitelist. To do this you would need to follow the process outlined at https://csp.withgoogle.com/docs/strict-csp.html -- basically, make sure that every <script> element has a correct nonce attribute which changes for every page load, or instead use CSP3 hashes for static scripts.
Your CSP would then look like:
... script-src 'nonce-[random-value]' 'strict-dynamic' 'unsafe-inline' https:; ...
If you use 'strict-dynamic', your script loader does not have to change because browsers will automatically trust scripts added to your page via programmatic APIs such as appendChild().