I would like to have the safest possible setup for my React front-end. Currently I run server.js out of the /build folder in deployment, so it is in a compiled, production state.
However, I can't use the following CSP which is fairly restrictive, if the js is bundled and inlined:
<meta http-equiv="Content-Security-Policy" content=
"default-src 'none';
object-src 'self';
script-src 'self';
worker-src 'self';
connect-src 'self';
img-src 'self' data:;
style-src 'self';
font-src 'self';
manifest-src 'self';">
I also get
Refused to apply inline style because it violates the following Content Security Policy directive: "style-src 'self'".
Since some node_modules like Draggable seem to dynamically inline styles.
What is an approach for me so that I can keep my code fairly obfuscated to deter attackers as well as a strong CSP? I've heard a webpack plugin might help but I don't really understand how that works in the build pipeline.
I believe I resolved this by changing from true to false the INLINE_RUNTIME_CHUNK value in .env. There is a little more info in this answer: How to use React without unsafe inline JavaScript/CSS code?
Also consider breaking up into .env.development and .env.production: What is the difference between .env.local and .env.development.local?
You can use this approach too https://cssinjs.org/csp/?v=v10.8.2. Implement a ExpressJs server that generates the headers and inject them into the react application. Keep in mind that many libraries already come with csp support and you must apply the generated nonce.
Related
I'm just saying that this is not the problem SecurityError: Blocked a frame with origin from accessing a cross-origin frame. I use single-spa and I was given the task to add one of the single-spa projects to a regular react project using iframe, I don't need to pass any data, just display it. But after I add the single-spa project in an iframe on another project (domains are different) I get the following error. The first thing that came to my mind is that the problem is in the Content-Security-Policy, it goes to the root-config in the index. jsx -<meta http-equiv="Content-Security-Policy" content="default-src 'self' https: localhost:*; script-src 'unsafe-inline' 'unsafe-eval' https: localhost: *; connect-src https: localhost:* ws://localhost:*; style-src 'unsafe-inline' https:; object-src 'none';worker-src 'self' blob:">, found - https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/frame-ancestors, but I don't understand how to pass it to this project to make it work, I tried it through webpack, it doesn't work. I would be VERY grateful to anyone who can help me figure it out.
I've been trying to lock down our production release to satisfy CSP requirements and have been successful except for when it comes to the styled-components script tag.
Nonce is not really an option with an SPA so a sha seemed the best solution, and the content of the offending script tag does not change.
The site is delivered by a simple Express server which sets the required headers and includes the sha for what is an empty tag (which is how the styled-components style tag renders):
Content-Security-Policy: script-src 'self'; img-src 'self' https://*; style-src 'self' 'sha256-47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=' fonts.googleapis.com; child-src 'self'; frame-ancestors 'self'; font-src fonts.gstatic.com
And the script tag being targeted in html head:
<style data-styled="active" data-styled-version="5.3.3"></style>
This performs correctly for Chrome, Edge and Firefox but the app fails to render on Safari with the following console error:
Error: An error occurred. See https://git.io/JUIaE#17 for more information.
In turn this refers to the error:
CSSStyleSheet could not be found on HTMLStyleElement. Has styled-components' style tag been unmounted or altered by another script?
I'm not entirely sure what this message is sugesting.
My only other thought is related to the use of the CSSOM APIs to inject styles (the reason for the styled-components script tag appearing to be empty), but I have no evidence that this is the problem for Safari.
I followed this article to add CSP to my existing react app. I did all the steps written in "Using inline script or style" there and here is my config-overrides.js file:
const { override } = require('customize-cra');
const cspHtmlWebpackPlugin = require('csp-html-webpack-plugin');
const cspConfigPolicy = {
'default-src': "'none'",
'base-uri': "'self'",
'object-src': "'none'",
'script-src': ["'self'"],
'style-src': ["'self'"],
'img-src': ["'self'"],
};
function addCspHtmlWebpackPlugin(config) {
if (process.env.NODE_ENV === 'production') {
config.plugins.push(new cspHtmlWebpackPlugin(cspConfigPolicy));
}
return config;
}
module.exports = {
webpack: override(addCspHtmlWebpackPlugin),
};
To test if it works I built the app with npm run build (as CSP is just added to production build) but before, to test if it works I added jquery to the script inside index.html:
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
Unfortunatey the jquery is added to build, CSP didn't block it.
Am I doing something wrong? Or I misunderstand the content security policy?
Actually you don't need a Package to achieve that, you can just add a <meta> tag as your first element to <head> block inside index.html founded in public folder.
As for jQuery issue, my guess that maybe a hash or nonce that auto generated by csp-html-webpack-plugin is referring to jQuery which could lead to allow it?
Also, please note that using unsafe-eval eval is considered unsafe. And should be avoided.
With that in mind, please note that if you're going to test your site security in some online security tools, it will basically Fail. Yes, adding a <meta> security tags could be enough for basic protection though, you may consider using server-side HTTP Headers for other security vulnerabilities. Actually meta tag is not needed, it's optional.
For instance, as for CSP policies, I've deployed a test react app using <meta> method, when testing on immuniweb.com or gf.dev, you'll see that there is No CSP policy! though, it works fine, see test Here
So if you can configure your server environment, I encourage you to do that. You could use Express with express-csp-header and/or using Nginx as a reverse proxy
If you can't, try setting your <meta> its fairly easy:
Syntax will be:
<meta http-equiv="Content-Security-Policy" content="directive 'source';">
Just like key-value pair where directives are the keys i.e. base-uri, script-src, style-src and sources (with single quotations marks) are the values i.e. self, none, unsafe-inline
See Directive Reference List | Source Reference List
For example since you are using React and React is using inline scripts <script></script>, you'll need to add 'unsafe-inline' to your script directive like so:
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; script-src 'self' 'unsafe-inline'; style-src 'self'; base-uri 'self';">
If you were to add some inline CSS or going to use a library like styled-components, you'll need to add 'unsafe-inline' to your style directive as well:
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; base-uri 'self';">
If you like to allow some external URLs like google font or analytics:
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; script-src 'self' www.google-analytics.com 'unsafe-inline'; style-src 'self' https://fonts.googleapis.com 'unsafe-inline'; font-src 'self' https://fonts.gstatic.com; base-uri 'self';">
Read more about directive and rules here You can also generate your own policy Here
I've got reactJs app which is deployed both as web and mobile app.
Mobile app is based on Cordova (ver 7.1.0).
Everything works fine except local resources (ones located in www/subdirs) are not found, despite they are there.
What is even more strange, some resources e.g. fonts are found, some- were found before (i18n files), but all of a sudden disappeared. Moreover, hardcoded in html images are found as well:
<img src='images/foo.png'/>
but exactly the same code being dynamically added with reactjs fails to reach the image.
Those resources which were found get resolved to proper url: file:///android_asset/www/subdir/resource.png
Those which are not- resolved to wrong path: file://subdir/resource.png
i tried both leading slash, no leading slash, setting html base tag- nothing
Does anybody have any idea what am i doing wrong?
thank you in advance
UPDATE:
here's security policy:
<meta http-equiv="Content-Security-Policy"
content="default-src 'self' 'unsafe-inline' data: gap: https://ssl.gstatic.com 'unsafe-eval'; connect-src *; style-src 'self' 'unsafe-inline'; media-src *">
Silly me! the browser worked like a charm and resolved all the resources exactly as it's supposed to. The problem was caused by react router which upon app load changed location from host/context/section to host/section so all resources which had to be loaded after that were resolved to incorrect path: host/section/resource instead of host/context/resource.
The solution is either fix initial location switch or add tab to the header
I have a PhoneGap app that I am using AngularJS with. I'm using a pretty simple $http call to my Node backend:
$http.get("http://localhost:6969/random")
.then(function(response) {
$scope.images = response.data;
});
No matter what, PhoneGap never hits the backend. I have tested it in a normal browser and it works as expected.
I have obviously read a bunch about it and most people fix it using whitelisting, but in my config.xml, my whitelisting is about as open as can be:
<plugin name="cordova-plugin-whitelist" source="npm" spec="1.1.0" />
<allow-navigation href="*" subdomains="true" />
<allow-intent href="*" subdomains="true"/>
<access origin="*" subdomains="true"/> <!-- Required for iOS9 -->
What do I have to change? Been wrestling this bug for a few days off and on and it's a bit annoying to not be able to actually create new cool features in my free time.
EDIT: I am serving the app using phonegap serve and testing it using the PhoneGap Developer App.
I would suggest your Content Security Policy might need to be modified to include a connect-src clause to specify where Ajax requests can go to.
Taking the CSP meta tag that you posted in the comments:
<meta http-equiv="Content-Security-Policy" content="default-src *; style-src 'self' 'unsafe-inline'; script-src: 'self' 'unsafe-inline' 'unsafe-eval'" />
I would suggest amending this to open up Ajax requests to anywhere, see if that helps then reign it in to just domain(s) you want to support after.
Suggested CSP would be:
<meta http-equiv="Content-Security-Policy" content="default-src 'self' data: gap: https://ssl.gstatic.com 'unsafe-eval'; style-src 'self' 'unsafe-inline'; media-src *; connect-src *">
If that works and you want to lock down to just one domain later you'd want something like:
<meta http-equiv="Content-Security-Policy" content="default-src 'self' data: gap: https://ssl.gstatic.com 'unsafe-eval'; style-src 'self' 'unsafe-inline'; media-src *; connect-src http://api.mydomain.com">
Additionally I think you will need to change your app's code to connect to your server by hostname or IP address, so so that on the device it doesn't confuse 'localhost' with the device itself and try to make a connection to port 6969 on the device.
So:
$http.get("http://localhost:6969/random")
May need to become:
$http.get("http://myhost.mydomain.com:6969/random")
Or
$http.get("http://xxx.xxx.xxx.xxx:6969/random")
There's some resources on this online:
Content Security Policy page
A blog post I wrote on this topic