What does the CSP nonce implementation look like? - reactjs

I've been reading CSP stuffs recently, but I failed to find a clear explanation or solid example about how nonce gonna work. I tried to avoid using unsafe-inline for security concerns.
So far, my understanding is like the following:
A server will generate a new nonce every connection
A client gets this nonce by a certain way and inserts this string into each script tag
However, I have no idea how the client will get this value which is dynamic and unique among each connection. What is a certain way be like? Should I implement an extra API to get this value?
I used React.js and there's a Google Tag Manager snippet in my HTML head, if someone can provide the example related to these would be perfect.

React applications is a SPA(Single Page Application) so content is loaded using XMLHttpRequest() and inserted without page reloading. Therefore 'nonce-value' is not used since you have no way to generate a new 'nonce' each time page refreshing.
The 'nonce' can be used when SSR (Server Side Rendering), in this case server can gererate fresh 'nonce' value and insert it into HTML code been sent (into <script nonce='value'>, <script src ='some_url' nonce='value'>, <style nonce='value'> and <link href='some_url' nonce='value' rel='stylesheet'>).
For this reason, React applications use 'hash-value' to allow inline scripts and styles.
For example react-static-plugin-csp-meta-tags package adds a CSP meta tag to your html files and adding hashes for all inline script and style tags.

Related

How to set dynamic metatags in ReactJS to get nice share links?

The app
The application was made using ReactJS, React Router Dom, Styled Components and Redux ducks.
The backend we consume is also made by us using Amazon Amplify and GraphQL.
The goal
We need to define the meta tags of one of the application pages so that it is possible to share personalized links to users
in social networks using OpenGraphic meta tags and the like.
The problem
The project was made in ReactJS and ReactJS has only one HTML page as root (/public/index.html), in this way, everything is generated with Javascript in a root tag, and when it arrives in the browser it is transpiled, as we already know. The problem is that the crawlers responsible for understanding the meta tags are not able to understand Javascript and end up not finding the dynamic data that I am defining on the page that I need to share the link on. They understand that there is one html file and only.
Attempts to resolve the issue
1) Define the meta tags in the /public/index.html file itself
This solution doesn't work because the data we are using is dynamic and the index.html file is a static file
2) Using react-helmet
The solution allows meta tags to be defined, but as already mentioned, crawlers don't understand JS. So, despite being on the page, the meta tags do not appear when sharing the link.
3) Using some SSR technology
This is a possible solution, but we were unable to integrate any SSR Framework into React. And it is not feasible to change the base technology of the project. We can't just switch from React to Next, for example, as the project is already complete.
4) Using a small server made with express.js along with the React application to replace the meta tags in index.html with string.replace() simulating something like an SSR
This solution works, but it causes two requests to be made every time the page is accessed, once by express.js and once on the front-end side by React. Due to the number of requests increasing, this solution was discarded. But if necessary, you can do it. In this case it is also necessary to check if Amplify can keep the application and the small server running in the same project.
5) Using react-snap with react-helmet
React-snap allows you to create html snapshots of the pages of a React project based on their routes and links, this added to react-helmet generates a perfect solution for links to be treated well by web crawlers when they are shared. But the solution doesn't work with dynamic routes. For example, /your-route/:id is a dynamic route that expects an id to be fully defined. React-snap gets lost when trying to create a snapshot of a route that only exists when the id is set. Unfortunately, this solution doesn't work.
These were the solutions we used to try to solve the problem, but it was not possible yet. Probably attempt 4 would be the most ideal to solve the problem. But we are looking for the best way that will not generate reworks and future problems. If someone knows a better way to do that, would help us a lot!

Sharing on social media, the URL does not render any meta data

We have built a project (Web Application) in React .net core using react in client-side rendering.
We've used react-helmet for dynamically assigning meta tags.
The issue being when the app renders in the browser. The browser gets only the static HTML on initial load which does not include the dynamic meta tags we have set. However on inspecting you get those meta tags under "Elements".
Also, if we use these URL for sharing on any social media, like WhatsApp or Facebook, the URL does not render any metadata as it should.
Tried searching for solutions to our problem, the most obvious answer we came across was to try server-side rendering instead. We get that, but it is not a solution to try out at this juncture when we're ready with app to roll it out.
Others we came across were "react-snap", "react-snapshot", but no luck
with react-snap, it requires to upgrade React's version to 16+, which we did but I guess not all dependencies were upgraded, there was an error saying "
hydrate is not a function
(hydrate concerns the react-dom)
With react-snapshot, we could not find the necessary type definition, which is required in react .net core to function properly
Please guide for the next probable step (except the paid ones like prerender, etc)?
Main goal: Social Applications should render the meta data when we paste/share the URL within them.
Prerender is the only solution.
I used a node dependency called "prerender" -> https://github.com/prerender/prerender
It works enabling a web server wich make http requests. Assigning value to a boolean: window.prerenderReady = true; in your website tells your server when the page is ready to "take the photo" and it returns the Html when so. You need to program an easy script that parses all the site urls and save those html contents to files. Upload them to your server and using .htaccess or similar target the crawlers external-hit-facebook,twitterbot,googlebot, etc.. to show them the prerendered version and 'the real site' to the rest of user-agents.
It worked for me.
The meta tags for Open Graph need to be present in the HTML which is sent back to the client when fetching a URL. Browsers or bots will not wait until the app is rendered on the client side to determine what the metatags are - they will only look at the initially loaded HTML.
If you need the content of your Open Graph metadata to be dynamic (showing different content depending on the URL, device, browser etc.) you need to add something like react-meta-tags into your server code.
There are no type definitions available for any of the react meta tags libraries, but you can add your own. It can be a bit tricky, but check out the official documentation and the templates they have provided to get started.
If you don't need it to be dynamic, you could add the tags into the static parts of the <head>-tag in your index.html.
I had the same issue today. I had two React Web applications that need this. Here is how I solved it:
put your preview image in the public folder
still in public folder, Open index.html, add the line <meta property="og:image" content="preview.png"/>
or <meta property="og:image" content="%PUBLIC_URL%/preview.png"/>.
Go to https://www.linkedin.com/post-inspector/ to check if it works.
I hope this would help!

How can I set a nonce in Material-UI for the Content-Security-Policy?

I have a React App using Create-React-App (react-scripts) and Material-UI. I would like to apply a strong Content-Security-Policy for my app which does not allow unsafe inline styles.
I would like to set the CSP-Header server-side with a nonce present which can be done easily. However, Material-UI sets certain inline <style> tags dynamically at runtime without a nonce value as attribute.
I´ve seen the documentation on the Material-UI website under guides and csp. They seem to provide a solution. However, that solution is for server-side-rendering of the HTML, which I am not using. I am using Create-React-App and deliver the HTML, CSS and JavaScript statically.
Does anyone know how that can be achieved?
The JSS CSP docs explain this much better than the MUI CSP docs, and provide examples for Express and Webpack. Basically, you need to set the same "nonce" in a special <meta> property called csp-nonce (which is read dynamically by JSS) and in your Content-Security-Policy header (either via another <meta> or a HTTP header) of public/index.html:
<meta http-equiv="Content-Security-Policy" content="default-src: 'self';
style-src: 'self' 'nonce-xxxxxxxxxxxxxxxx=='">
<meta property="csp-nonce" content="xxxxxxxxxxxxxxxx==" />
If you can do this dynamically in whatever serves your index.html, then it is secure. (Obviously, an injected script could also read the nonce dynamically, but if that has happened, you've already lost). If you have to use a fixed value (e.g. because you're serving from a CDN), it's not secure but still arguably better than style-src: 'unsafe-inline', since an attacker at least needs to use your site-specific nonce.
As a hybrid approach when using a CDN, you can set a random nonce when the CDN fetches the page from the origin, as demonstrated here with an AWS Lambda#Edge. Then your nonce is only vulnerable in a specific region for the CDN cache TTL (which should be short for a mutable resource like index.html).
It works the way it is. The nonce value is set, even though it is not shown in the browser.

isomorphic/universally-rendered React 15 app breaks with Cloudflare HTTP Proxy ("orange cloud")

I have an isomorphic/universal React app, meaning it is rendered on the server by the same JS that powers the client-side, Single Page App user experience.
I configure this app's DNS using Cloudflare, and use their "orange cloud" feature to accelerate and protect my site's traffic, as explained in the following graphic and in their support article:
From the linked-to article: "Cloudflare can operate in two modes - DNS only (unproxied; 'grey cloud') and as a HTTP proxy ('orange cloud') with our security, CDN & performance features."
I have discovered that running my app with React 15 and Cloudflare's "orange cloud" HTTP Proxy feature results in an error:
reactProdInvariant.js:31 Uncaught Error: Minified React error #32; visit http://facebook.github.io/react/docs/error-decoder.html?invariant=32&args[]=2 for the full message or use the non-minified dev environment for full errors and additional helpful warnings.
The text of the linked-to bug:
Unable to find element with ID 2.
This ID probably refers to the head tag of my page:
<!doctype html>
<html lang="en-us" data-reactroot="" data-reactid="1" data-react-checksum="-1233854461"><head data-reactid="2">...
I do NOT get this issue with React 14.
I do NOT get this bug when using the raw, un-DNS-ed address of my app, nor do I get it when I switch to the "grey cloud" to use Cloudflare as only a DNS service.
I do NOT get this issue when I disable server-side rendering.
When I google the text of the linked-to "actual" error, I find this Github thread that confirms that this has something to do with server-side rendering.
However, my situation differs slightly: that thread's author encounters the error with an "unable to find element" ID referring to a style tag, whereas in my case the unfound element ID refers to the head tag.
When I google the text of the production "wrapped" error, I find this Github thread that confirms that this has something to do with Cloudflare's HTTP Proxy. This comment says: "If you're using CloudFlare, please disable auto-minification of HTML."
So far, I can't figure out how to do that. It's hard for me to find good information on what exactly Cloudflare does with their HTTP Proxy, and how I can configure it.
Questions:
Why exactly does this bug happen?
Where can I find information about what exactly the "orange cloud" does?
What is the best way to fix this problem while maintaining the benefits of Cloudflare's HTTP Proxy?
React on the server needs to render the application to a string of HTML that makes sense to the browser. At the same time, React in the browser needs to read this HTML and understand it in relation to your JS code: it needs to identify which DOM tree came from which React component, in a very detailed way. Thus, in effect, the intermediate HTML is a serialization format between React on the server and React on the client with an additional requirement for it to make sense for the browser even in the absence of React.
When you enable advanced Cloudflare functionality, it treats the HTML as a “regular” HTML, not fancy server-side rendered compnents. My baseless speculation on one thing that could be going wrong is stripping HTML comments. In general, this is a natural thing to do for minification. But React uses HTML comments to put a <!-- empty --> placeholder where a React component returns null. Naturally, stripping these breaks React.
Cloudflare is there to make serving websites faster at a lower cost. They have a wide variety of tools to achieve that, see their introduction guide. Minifying HTML is completely natural and it is unfortunate it breaks your use case, but this is what we get when the meaning of nodes and attributes and flexibility changing them in our HTML is not strictly defined.
I think the most straight-forward way for you to move forward for now is to disable HTML minification in Cloudflare settings.

Universal React without Javascript

What are best practices for invoking different CSS when the client for my server-rendered universal React page has Javascript disabled?
I've tried including Modernizr with className="no-js" on the html tag but it wasn't edited.
I've managed to get something to work as shown below but there must be a better way.
<link rel="stylesheet" href="/public/css/script.css" />
<noscript>
<link rel="stylesheet" href="/public/css/noscript.css" />
</noscript>
I've looked for examples but haven't found anything relevant.
In a server rendered application, you can have one of many data sources:
query param
cookie
POST data
The only input for server side code is the request made when loading up a page in your app in your browser. You may choose to configure the server during its boot (such as when you are running in a development vs. production environment), but that configuration is static.
The request contains information about the URL requested, including any query parameters, which will be useful when using something like React Router. It can also contain headers with inputs like cookies or authorization, or POST body data.
In a script/noscript mapping, you can setup one of several defaults:
Render noscript as the default, then override it by injecting script specific CSS via the CSSOM
Serve a noscript specific route as the default, then jump to the script route based on a cookie
Create a form which posts scripting capabilities to the server, then use that as a jump page to avoid noscript/script in the markup
References
redux / Server Rendering: Processing Request Parameters
Rendering noscript version serverside without reuse markup warnings in React

Resources