Sitemap

CSP Handling with React Js, Craco & Handling inline styles coming from API to avoid unsafe-inline

6 min readFeb 17, 2023

--

What is CSP?

CSP is a security standard that prevents cross-site scripting(XSS), clickjacking and other code injection attacks. It is used to avoid the malicious code injected by someone who wants unauthorised access or data in the website.

With CSP, you can define the data sources from which the web application will get rendered. You can define the data sources in CSP directives
i.e meta tag into your HTML Headers as follows,

<meta http-equiv=”Content-Security-Policy” content=” script-src ‘self’; style-src ‘self’ *.xyz.com ‘nonce-<nonce-value>’; “>

Implementation of CSP in React

Usually adding CSP to a react application is easy. we can follow the following steps.

Step 1: Install the dependencies

npm install react-app-rewired customize-cra csp-html-webpack-plugin --save-dev

Step 2: Replace the commands in package.json under the scripts json object to as shown below.

 "scripts": {
"start": "react-app-rewired start",
"stop": "react-app-rewired stop",
"build": "react-app-rewired build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},

Step 3: Add the config-overrides.js file given below into project root dir.

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'"]
};

function addCspHtmlWebpackPlugin(config) {
if(process.env.NODE_ENV === 'production') {
config.plugins.push(new cspHtmlWebpackPlugin(cspConfigPolicy));
}

return config;
}

module.exports = {
webpack: override(addCspHtmlWebpackPlugin),
};

In this file we are customizing the CRA application by using csp-html-webpack-plugin in which you can define the CSP config policies and you can override the webpack config.

You can also take help from the following website to understand the CSP directives

https://content-security-policy.com/

Step 4: Update the index.html in the public folder. Add a meta tag as a child element to head tag in the index.html

<meta http-equiv=”Content-Security-Policy” content=””>

Step 5: Build the react app:

npm run build

After building the react app you need to serve the build with serve

  serve -s build

You will be able to see that the CSP has been added to your website with nonce value created for js and css files

CSP enabled HTML

In the above application, when you have the inline styles on react component like in the following example. React will take care of rendering the styles for that component. It does so by combining all the css from your application into one file and assigns a nonce value to it.

 <div>
<div style={{ color: 'red' }}}>Inline CSS React block</div>
</div>

But In the following example we have a HTML element as a constant that we want to render on the webpage. This constant can also be coming from API or some constant files.

export default function CspFeature() {

const [html, setHtml] = useState('');

useEffect(() => {
const html= `<div style="color: red; background-color: yellow; height: 100%; width: 100%" >
Inline CSS React block
</div>`
}

return (
<div>
<div dangerouslySetInnerHTML={{ __html: html }} />
</div>
);

}

In this case the application will throw a below CSP error.

CSP Inline Style Error

So the application is only secure when there is no inline CSS in your react application from any kind of source like constant or API, or file.

But you can use inline-css in your application using unsafe-inline in your style-src directive. However, this does not secure your application because using unsafe-inline in the CSP directives will make your application vulnerable.

Here, we can take a nonce value from the HTML header and assign it to the inline style. But the major problem is we need to separate the CSS and HTML from each other. To implement this, we can use the following approach.

CSP handling for inline styles integrated with HTML coming from API, file or constant.

So in the above section we have already added the CSP to our react app. Yet, we should avoid the use of unsafe-inline. Hence, allowing the styles or script from other internal sources becomes unsecure. Let’s now examine how to enable the CSP in the subsequent scenarios:

for example we have following HTML as a API response or input from the user or a constant as follows,

const html = `<div style="color: red; background-color: yellow; height: 100%; width: 100%" >
<div style="color: blue; background-color: green; height: 90%; width: 90%" >
<div style="color: blue; background-color: navy; height: 80%; width: 80%" >
<div style="color: blue; background-color: white; height: 700%; width: 70%" >
<div style="color: blue; background-color: black; height: 600%; width: 60%" >
<div style="color: blue; background-color: orange; height: 500%; width: 50%; text-align:center" >
CSP is working
</div>
</div>
</div>
</div>
</div>
</div>`
<div>
<div dangerouslySetInnerHTML={{ __html: html }} />
</div>

So here we can see multiple inline styles has been added on the div tags. but when we try to add this html into our react app by dangerouslySetInnerHTML attribute it will throw an error that

Refused to apply inline style because it violates the following Content Security Policy directive

In this situation, we can find it challenging to integrate HTML to ingest into our react app using dangerouslySetInnerHTML. So here the solution is to separate the CSS and HTML from each other. But what if you don’t have control over HTML. For example HTML is coming from a file, or HTML is coming from API. And still, you want to integrate it into your application. So in this situation, you can use the following approach.

Step 1: To separating styles and html tags into two different objects

We can use cheerio lib to separate the HTML and CSS. To use it in our component, we can install it using NPM and import it into our component.

https://www.npmjs.com/package/cheerio

npm install cheerio
import * as cheerio from 'cheerio';

Write a function which will take a HTML as a input and gives you the separate HTML and CSS in different objects.

function separateHtmlAndCss(html, nonce2){
const $ = cheerio.load(html);
const styles = {};
let nextId = 1;
$('[style]').each(function() {
const el = $(this);
const style = el.attr('style');
const id = `style-${nextId}`;
styles[id] = style;
el.removeAttr('style');
el.addClass(id);
nextId++;
});
const css = Object.entries(styles).map(([id, style]) => {
return `.${id} { ${style} }`;
}).join('\n');

console.log(css);
console.log($.html());
}

In the above code, we can see that we have got css object and $.html() as HTML, two different objects. So after getting HTML and css in two different objects we can use dangerouslySetInnerHTML to integrate the HTML into our react component but what about the CSS?

So to ingest the CSS into our react component, we can use the style tag. to add a styleRef div tag which will help to inject the CSS, we need to add a styleRef using useRef hook of the react.

const [html, setHtml] = useState('');    
const styleRef = useRef(null);
<div>
<div ref={styleRef} />
<div dangerouslySetInnerHTML={{ __html: html }} />
</div>

but here, we need to provide the nonce value to style tag otherwise, the CSP will reject the styles.

Step 2: To get the nonce value in react app dynamically

Note: here, we are considering the react app has the CSP enabled, and the nonce values has been generated at the time of the build.

To get the nonce value dynamically, you can use simple Javascript to fetch it from the meta tag of your HTML, as shown in the following example.

 const cspHeader = document.querySelector("meta[http-equiv='Content-Security-Policy']").getAttribute('content');

if(null !=cspHeader){
const cspDirectives = cspHeader.split(';');
let nonce = cspDirectives.find(directive =>directive.includes("'nonce-"))
let nonce2 = ''
if(null != nonce1){
nonce2 = nonce1.split("'")[3];
}
nonce2 = nonce2.substring(6)

Here you can find the nonce value, which is generated by build. Now we need to add this nonce value to the style tag of CSS into our react component

We can extend the separateHtmlAndCss function which we have created above to inject styles and HTML into our React app

function separateHtmlAndCss(html1, nonce2){
const $ = cheerio.load(html1);
const styles = {};
let nextId = 1;

$('[style]').each(function() {
const el = $(this);
const style = el.attr('style');
const id = `style-${nextId}`;
styles[id] = style;
el.removeAttr('style');
el.addClass(id);
nextId++;
});

const css = Object.entries(styles).map(([id, style]) => {
return `.${id} { ${style} }`;
}).join('\n');

console.log(css);
console.log($.html());

setHtml($.html());
const styleTag = document.createElement('style');
styleTag.innerHTML = css;
styleTag.setAttribute('nonce', nonce2);
styleRef.current.appendChild(styleTag);
}

This way, we can inject the HTML code from api and a css into a separate style tag into a CSP-enabled application using nonce value.

--

--

No responses yet