Preventing XSS in React Applications
Tags: react, secury
Published: 2020-03-22 18:30:29

Cross-site scripting (XSS) accounts for the majority of web applications security vulnerabilities. It lets attackers inject client-side scripts into web pages, bypass access controls, steal sessions, cookies, connect to ports or computers camera. 

## JSX Data Binding

To fight against XSS, React prevents render of any embedded value in JSX by escaping anything that is not explicitly written in the application. And before rendering it converts everything to a string.

A good example of how React escapes embedded data if you try to render the following content:

```jsx
function App() {
  const userInput = "Hi, <img src='' onerror='alert(0)' />";
  
  return (
    <div>
      {userInput}
    </div>
  );
}
```
The output in the browser will be: Hi, <img src='' onerror='alert(0)' />, rendered as a string with an image tag being escaped. That is very handy and covers simple cases where an attacker could inject the script. If you would try to load the same content directly in the DOM, you would see an alert message popped out.

## 3rd Party Libraries

With the more complex cases, where it is not enough just to render data through JSX, the risk is much higher. For example, the business requirements changed, and application now needs to accept user input with embedded data like styling bold, italic and etc. tags. Let's say we need to render user input: `<b>Hi React</b>` with the desired output: **Hi React**. 
This requires a direct injection into the DOM for data to be parsed as HTML, and it can be done by setting `dangerouslySetInnerHTML`:

```jsx
const userInput = <b>Hi React</b>;
return <div dangerouslySetInnerHTML={{ __html: userInput }} />;
```
This will solve business requirements that let user directly style the text, but at the same time it opens a huge risk and possibility for XSS atacks. Now if the evil user enters `<b>"Hi, <img src='' onerror='alert(0)' />"</b>` the browser will render this:

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/1il69y808h5zqx4y8pdc.png)

To avoid running dangerous scripts they should be sanitized before rendering. The best option is to use a 3rd party library, for example, popular and maintained library [dompurify](https://www.npmjs.com/package/dompurify) with zero dependencies sanitizes HTML. Improved code would now:

```jsx
import createDOMPurify from "dompurify";
const DOMPurify = createDOMPurify(window);

function App() {
  const userInput = "<b>Hi, <img src='' onerror='alert(0)' /></b>";
  
  return (
    <div dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(userInput) }} />
  );
}
```
The render content will look not: **Hi,** and an empty image tag will be escaped.

But do not rely on all parsing libraries. For example, using a popular [react-html-parser](https://www.npmjs.com/package/react-html-parser) at first will seem that it does the job, escapes the script from the image tag, but it would parse the following code:

```jsx
return (
  <div>
    {ReactHtmlParser('<iframe src="javascript:alert(0)"></iframe>')}
  </div>;
```
Which will result in running the script in `iframe` and invoking alert modal. Parsers do not scape all scripts, especially in `iframes`.

## Summing Up

Simple tips to follow for securing React application and minimizing threat for XSS attacks:
- Render data in JSX when possible to use implemented securities by React
- Sanitize data when using `dangerouslySetInnerHTML`
- Do not trust HTML parsers to escape vulnerable scripts
- Avoid direct data injection into the DOM