ReactJS Security Best Practices

Naresh Vunnam
WhatfixEngineeringBlog
4 min readMay 19, 2023
ReactJS Security Best Practices

React is a free and open-source front-end JavaScript library that is used for building user interfaces. React allows developers to build reusable UI components that can be easily rendered and updated in response to user interactions and changes in data. It uses a declarative approach to programming, meaning that developers can specify how they want the user interface to look and behave, and React takes care of updating the DOM (Document Object Model) to reflect those changes.

React doesn’t create any security vulnerabilities by itself. However, developers may enable some options as part of the implementation which could make applications vulnerable.

We might not think about every possible vulnerability, but we can definitely make our application more secure by mitigating the most common risks.

Following are some of the best practices we should follow to secure our React applications:

XSS Protection with data binding:

Use data binding with curly braces {} and React will automatically escape values to protect against XSS attacks. However, this protection only helps when rendering textContent and not when rendering HTML attributes.

Use JSX data-binding syntax {} to place data in our elements.

Do this:

<div>{data}</div>

Avoid dynamic attribute values without custom validation.

Don’t do this:

<form action={data} ……/>

Rendering HTML:

We can insert HTML directly into the DOM using dangerouslySetInnerHTML . These contents must be sanitized beforehand. Use a sanitization library such as dompurify on these values before placing them into the dangerouslySetInnerHTML prop.

What does react dangerouslySetInnerHTML?

It enables developers to directly insert HTML content within an HTML element found in a React app.

Thus, use dompurify when injecting native HTML codes into the react DOM:

Do this:

import purify from “dompurify”;
<div dangerouslySetInnerHTML={{ __html:purify.sanitize(data) }} />

Avoid using dangerouslySetInnerHTML without dompurify sanitize

Don’t do this:

<div dangerouslySetInnerHTML={{ __html:data}} />

Avoid direct DOM Access:

Always avoid accessing the DOM to inject content into DOM nodes directly. But if you do have to, use dangerouslySetInnerHTML to inject HTML and sanitize it before injecting it using dompurify.

Avoid using refs and findDomNode() to access rendered DOM elements to directly inject content via innerHTML and similar properties or methods.

Don’t do this:

this.myRef.current.innerHTML = userControlledValue;

Dangerous URLs:

URLs may contain dynamic script content. So, always validate the URL to ensure the links are http: or https: to avoid javascript: URL-based script injection. Use the native URL parsing function to validate the URL and match the parsed protocol property to an allowed list.

Do this:

function isValidaUrl(url) {
const parsed = new URL(url)
return [‘https:’, ‘http:’].includes(parsed.protocol)
}
<a href={isValidUrl(url) ? url : ‘#’}>Click here!</a>

Don’t do this:

<a href={userControlledUrl}>Click here!</a>

ex: <a href={javascript:alert(document.cookie}>Click here!</a>

Secure react server-side rendering:

Data binding will provide automatic content escaping when using server-side rendering functions like ReactDOMServer.renderToString() and ReactDOMServer.renderToStaticMarkup().

It is not safe to combine unsanitized data with the renderToStaticMarkup() output before sending it for hydration. Avoid concatenation of unsanitized data with the renderToStaticMarkup() output.

Don’t do this:

app.get(“/”, function (req, res) {

return res.send(

ReactDOMServer.renderToStaticMarkup(

React.createElement(“h1”, null, “Hello World!”)

) + otherData

);

})

Never serialize sensitive data:

We often set the initial state of our app with JSON values. This being the case, JSON.stringify() is a function which converts any data into a string even though it is vulnerable. Thus it gives freedom to an attacker to inject a malicious JS object which can modify valid data.

Don’t do this:

function renderFullPage(html, preloadedState) {

return ` <!doctype html>

<html>

<head>

<title>Example</title>

</head>

<body>

<div id=”root”>${html}</div>

<script>

window.__PRELOADED_STATE__ = ${JSON.stringify(preloadedState)}

</script>

<script src=”/static/bundle.js”></script>

</body>

</html> `

}

preloadedState: {“title”:”oh!”,”content”:”</script><script>alert(‘gotcha!’)</script>”,”restaurantId”:1,”id”:1}

Avoid Json Injection attacks:

It is possible to send JSON data along with server-side rendered react pages. So, try to replace < character with a gentle value (Unicode value) to prevent injection attacks.

Always try to replace HTML specific codes from JSON with its equivalent characters:

Do this:

window.__PRELOADED_STATE__ = ${JSON.stringify(preloadedState).replace( /</g, ‘\\u003c’).replace( />/g, ‘\\u003e’)}replace( /</g, ‘\\u003c’)

Avoid using dangerous library code:

The library code is often used to perform dangerous operations like directly inserting HTML into the DOM. So, avoid libraries that use innerHTML, dangerouslySetInnerHTML or unvalidated URLs. Also, configure Linters to detect unsafe usage of React’s security mechanisms.

Install linter configurations and plugins that will automatically detect security issues in our code and offer remediation advice. Use the ESLint React security config to detect security issues in our code base. This package helps to avoid security mistakes.

Detecting vulnerabilities in dependencies:

Some versions of third-party components might contain security issues. Thus, integrate SCA tools into CI/CD pipeline for security and compliance scanning to make sure that build jobs containing vulnerabilities are stopped early in the process before getting added to the artifactory and also update libraries at regular intervals or when the latest versions become available.

React had a few high vulnerabilities in its initial release unlike now. So, it is better to keep the react version up to date to avoid the use of vulnerable versions of the react and react-dom . Use the npm audit command to verify dependency vulnerabilities.

References:

https://snyk.io/blog/10-react-security-best-practices/

https://www.freecodecamp.org/news/best-practices-for-security-of-your-react-js-application/

https://pragmaticwebsecurity.com/articles/spasecurity/json-stringify-xss.html

--

--