React is arguably the most popular front-end development framework. As a full-stack developer, I personally prefer working with React in the front-end as it allows me to quickly build complicated views for applications.
The reasons for this convenience are the following:
- Starting and configuring a React application is as easy as calling `create-react-app <project name>` in your terminal.
- You can find thousands of open-source components that you can quickly integrate into your project. This allows you to just focus on your project’s core features and leverage on pre-built ones for the trivial aspects.
- Incorporating libraries such as redux and react-redux allows you to build highly complex states for your applications. This in turn gives you the ability to create more features.
Because of this, React has been maintaining its position as the most beloved web framework, according to a 2019 survey by StackOverflow:
The convenience offered by using React in development can then lead to developers taking for granted one important aspect – security.
React has a smaller attack surface than other frameworks like, say, Angular. However, this doesn’t mean we should not include security best practices into our development workflow.
Why it’s important to be aware of security vulnerabilities in React
Even if you’re the most careful programmer and you ensure your code is air-tight you have to remember that using open-source components can still open up your application to possible attacks. This is a price we pay for the convenience of simply installing such open-source components.
The demand for new features increases and building them through React can be the fastest and most efficient way. This then creates more risk for both users and organizations creating those applications.
Users are at risk because of their personal and financial data that can be stolen. Organizations are at risk of violating privacy regulations as well as having legal troubles because of user data breach. Security protocols should then be included right at the programming stage as well as in the full Software Development Lifecycle.
List of vulnerabilities to watch out for in a React project
This article isn’t meant to be an exhaustive list of all possible vulnerabilities to detect and fix in your React projects. What follows are the major things that we need to watch out for.
The DOM API allows us to set the `innerHTML` for an element. This, however, is a dangerous practice as it is a wide-open gate for XSS attack. React has provided a bit of safeguard with regards to this with the `dangerouslySetInnerHTML` prop.
The problem is that it didn’t remove the possibility of an insecure piece of code being written. For instance:
This displays the following empty input box and text (`h1`) with the value ‘Hello World’ in bold letters:
Now the vulnerability this exposes and can be taken advantage of is if an attacker decides to put a script tag in the input box:
The text “<script>alert(0)</script>” will be parsed by the component into an HTML code that it will attach to the DOM. It will then cause an alert message “0” to appear on screen once the user presses Enter.
Now this particular script isn’t at all dangerous (but irritating). But what if the malicious user inserts a script that can capture an authorization cookie from the user’s machine. The attacker can then use this authorization cookie to steal other users’ information and data.
How to Fix This
The most obvious fix would be to avoid using dangerouslySetInnerHTML prop on components that take in user input.
Another fix is to use libraries such as DOMPurify in order to sanitize user input and remove any malicious texts.
What this will do is that if the user types in the same dangerous script as the one prior to the fix, DomPurify will strip it out of the resulting DOM.
Similar to the previous vulnerability, an anchor tag (`<a>`) can allow malicious code to enter into the application. Let’s take a look at how this can happen with the following snippet:
This component will render a blank input box and initially an empty list of urls:
The user can then input links into the box and the component will render each one. For example, typing “https://google.com” into the box will display the link below the text “Links”:
The exact text (now a link) appears below the first one. As it is, it’s harmless. But if the user clicks on that link it will trigger the “alert” call.
As with the previous vulnerability, this can allow a malicious script to enter the code.
How to Fix This
A very simple way to ensure no malicious script will be accepted by the application is by whitelisting the kinds of input a user can make.
In the previous code snippet then, we make an adjustment to the `addLink` inner function:
Anything that doesn’t contain a protocol “http” or “https” will now terminate the function and won’t include that input into the list of links.
Server-Side Rendering Vulnerability
In server-side-render (SSR), the HTML (content) is rendered on the server side then delivered to the browser. For large applications, users can see a faster page load.
Another benefit of server side rendering is the ease of incorporating Search Engine Optimization (SEO). Since each page can be rendered from the server, it can have unique meta-tags and titles. This is very challenging to do in a client-rendered application.
Now since most React applications utilize state, a server-rendered component may then also need to include initial state. To handle complex state-management we normally use Redux. In working with server-rendered applications, we also need to initialize state over on the server.
This is a rather complicated process but Redux has suggested a clear set of steps. These include injecting initial component HTML and State.
How to Fix This
The preceding image already includes a possible fix. It is to use Regular-Expressions to capture any unwanted formats.
It’s impossible to catch every single vulnerability manually though it is essential to practice good and secure coding habits. We should also leverage on tools that can scan our codebase and report and/or fix any vulnerabilities.