On React App Security

Konstantin KomelinKonstantin Komelin

Imagine, you inherited a React app from another developer and you need to make sure the existing code is secure. In this post I'll give you a checklist which can help to secure your app.

First of all, let's make a convention - any data source you utilize is not safe by default. For example: a third-party API, a database with user entered content, etc.

Secondly, cross-site scripting vulnerability (XSS) is one of the most popular and dangerous vulnerabilities in web apps of all times, so I will be primarily focusing on XSS here.

Now let's get down to business. And here is my security checklist:

1. Check dependencies for security issues

Start from checking your project dependencies for known vulnerabilities and fix them.

npm audit

You may even add the dependency checks to your project build pipeline to do it regularly.

There are also other security vulnerability databases and tools, such as, for example, snyk which you may want to check out too.

2. Find dangerouslySetInnerHTML calls and sanitize passed HTML

Ideally, you should use <div>{ data }</div> to render any piece of data in a React app. This way the data string is automatically escaped and your users are safe from XSS. However, sometimes we want to render formatted HTML text instead and we use dangerouslySetInnerHTML to do it.

const data = '<b>Bold text</b>';
<div dangerouslySetInnerHTML={__html: data} />;

But it's strongly recommended to sanitize the passed data.

There is a great library called DOMPurify aimed to help with HTML data sanitization.

You may use it directly or through my isomorphic wrapper which unifies use of DOMPurify on client and server, for example when SSR is enabled.

npm install isomorphic-dompurify
const data = DOMPurify.sanitize('<b>Bold text</b>', {ALLOWED_TAGS: ['b']});

Note, it's a good idea to encapsulate sanitizing logic to a separate function or/and use HOC pattern, so that you can adjust sanitization settings or switch to another sanitizing library by only changing code in one place.

3. Find direct DOM manipulations and sanitize passed HTML

Firstly, search for innerHTML and sanitize what's assigned to it.

Then pay special attention to the places with ref="", for example:

<input ref={...} />

Or code pieces with:

ReactDOM.findDOMNode(component)

If you directly pass HTML to the DOM, just make it safe too.

4. Make sure user input is validated

If you allow your users to enter urls, emails or phones, don't forget to validate user input to prevent XSS attacks. You may google for a validation library or write your own regular expressions for that.

Just bear in mind, If you write your own code to validate user input, prefer the whitelist approach to the blacklist approach. The whitelist approach means - "first deny all; then allow some". The reason is that new threads are discovered from time to time, so you can't blacklist all future threads which are not known yet. For example, instead of filtering out urls with "javascript:", I would only allow urls with correct protocols, such as "http://", "https://", etc.

Also, don't ignore server-side validation. It's a good practice to have validation on both, client side and server side.


Of course, this checklist is quite basic and I'm sure you're familiar with most of the recommendations but who can guarantee that a previous developer of your project was?

I think it'd be wise to complete the list with your own steps and add it to your project/company documentation, so that every new developer can keep the best practices in mind while coding. Having development standards and documentation is an indicator of developer maturity, by the way.