I am working on a React app with server-side rendering. The app uses Redux for state management and Redux Saga for async actions. In pseudocode, what I am doing on the server side right now is:
1) initialize Redux store and run Redux saga on it
2) wait until all necessary data is fetched
3) render React component to string
4) send rendered React component to the client
(along with the populated store for rehydration on the client side)
My problem is with step 4. Right now, I am passing the rendered React component and the store to an ejs
view that looks roughly like this:
<html>
<head>
<meta charset="utf-8">
<title>Test</title>
<link rel="stylesheet" type="text/css" href="/styles.css">
<script>
var __data = <%-JSON.stringify(store) %>;
</script>
</head>
<body>
<main><%- app %></main>
<script type="text/javascript" src="/vendor.js"></script>
<script type="text/javascript" src="/bundle.js"></script>
</body>
</html>
(in the code above, app
is the rendered React component, and store
is the Redux store)
Trouble is, the above code, specifically
<script>
var __data = <%-JSON.stringify(store) %>;
</script>
does not escape html, so if my store contains a string with the <script>
tag, it will be valid html, and opens the app to XSS attacks.
If instead of <%- %>
use <%= %>
like so: var __data = "<%= JSON.stringify(store) %>";
, I get a string that is invalid JSON:
"{"greetingReducer":{"message":"Hello world!"} etc.
which I am not sure how to transform back to valid JSON.
So my question (silly as it may seem) is: what’s a proper way of passing a Redux store (HTML-escaped string of a JSON object) in the HTML template to the client and how to transform that string back to a JavaScript object on the client.
You can try serialize-javascript if you're ok with including another module to do this. It has support to escape harmful HTML strings.