How to prevent XSS attacks when using dangerouslySetInnerHTML in React
Tags: react, xss, dangerous, eslint
Published: 2019-02-04 21:11:04

---
title: How to prevent XSS attacks when using dangerouslySetInnerHTML in React
published: true
description: 
tags: react, xss, dangerous, eslint
---

   ![XSS hero image](https://thepracticaldev.s3.amazonaws.com/i/x2fc02w53jwn7cel1gmj.png)

This article intends to show one of the techniques we use to mitigate cross-site scripting (XSS) attacks at [Jam3](https://jam3.com). These vulnerabilities may appear when `dangerouslySetInnerHTML` is wrongly used, and our goal is to detect it ahead of time and to clean up untrusted values.

# **Dangerously Set innerHTML**

This feature is designed to present and insert DOM formatted content data into the frontend. The use of the feature is a bad practice, especially when dealing with user inputs and dynamic data. You must consider its vulnerabilities in order to prevent [XSS attack](https://www.owasp.org/index.php/Cross-site_Scripting_(XSS)). 

**"Easy" to make thing safe** is one of React philosophy. React is flexible and extendable which means that the bad practice can be turning into the best practice. Sanitizing props value is one obvious option and strongly recommended.



# **XSS attacks**

Cross-site scripting (XSS) allows attackers(hackers) to inject malicious code into a website for other end-users. By doing this, attackers may have access to personal data, cookies, webcams, and even more. Read more about [Cross-site scripting](https://www.owasp.org/index.php/Cross-site_Scripting_(XSS)).

Copy `https://placeimgxxx.com/320/320/any" onerror="alert('xss injection')` and paste it in the input field in the xss injection example below:
[![Edit k9vxk9ppyo](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/s/k9vxk9ppyo?autoresize=1&moduleview=1)


### **Preventing XSS**
This issue is not restricted to React; to learn how to prevent it in your web development OWASP has a good [prevention cheat sheet](https://www.owasp.org/index.php/XSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet). One approach to prevent XSS attacks is to sanitize data. It can be done either on the server-side or the client-side; in this article, we will focus on the client-side solution.

### **Preventing XSS with dangerouslyInnerSetHTML**
Sanitizing content in the frontend when using `dangerouslySetInnerHTML` is always a good security practice, even with a trusted [source of truth](https://en.wikipedia.org/wiki/Single_source_of_truth). For example, another development team in charge of maintaining the project changes the source of truth without realizing how it could impact the site. A change like that may cause a critical XSS vulnerability.

At Jam3 we avoid using `dangerouslySetInnerHTML` whenever possible. When it's required, we **always** apply sanitization security layers on both the back-end and front-end. In addition, we created an [ESLint](https://eslint.org/) rule called `no-sanitizer-with-danger` inside [`eslint-plugin-jam3`](https://github.com/Jam3/eslint-plugin-jam3) to detect improper use of `dangerouslySetInnerHTML`.


# **ESLint rule**
I assume that you are already familiar with ESLint. If not, [Get Started](https://eslint.org/docs/user-guide/getting-started).

```
$ npm i eslint eslint-plugin-jam3 -save-dev
```
Extend `pluginsin` the .eslintrc config file by adding `jam3`. You can omit the eslint-plugin- prefix. Then, configure the rules by adding `jam3/no-sanitizer-with-danger` to the rules. Note: error level 2 is recommended. With this option, exit code will be 1. error level 1 will give warning alert, but does not affect exit code. 0 means to turn the rule off. The plugin will check that the content passed to `dangerouslySetInnerHTML` is wrapped in this sanitizer function. The wrapper function name can be also be changed in the JSON file (`sanitizer` is the default wrapper name).

{% gist https://gist.github.com/DonghyukJacobJang/942d4326ef554c507a95d8fe690ffb97 %}


### **How to use it**
Here is an **unsafe** way of using dangerouslySetInnerHTML.

{% gist https://gist.github.com/DonghyukJacobJang/27838e3832b5da4493605c876f799eb6 %}


![dangerouslySetInnerHTML without sanitizer](https://thepracticaldev.s3.amazonaws.com/i/zezurrk5zc8lokgl2xgn.png)

Once the rule is enabled, your code editor will alert the lack of a sanitizer in `dangerouslySetInnerHTML`. For the purpose of this article we use [dompurify](https://github.com/cure53/DOMPurify), you can find an extended list of available sanitizers at the end of the article.

The sanitizer wrapper must have a name, for the purpose of this article we are creating `const sanitizer = dompurify.sanitize;`. However, it is recommended to create a sanitization utility to abstract your chosen sanitizer.

{% gist https://gist.github.com/DonghyukJacobJang/75c134b871fbe59f6eeae4968551c9fb %}


# **Sanitizer libraries**

Our team has researched and tried many sanitizers and can recommend these 3 libraries.

[**dompurify**](https://github.com/cure53/DOMPurify)

* Strip out all dirty HTML and returns clean HTML data
npm Weekly download 50k+
* 40 contributors 
* Earned 2800+ GitHub ⭐️
* 5.6kB MINIFIED + GZIPPED

{% gist https://gist.github.com/DonghyukJacobJang/29844b7074af5dc5dee323ddd368c459 %}


[**xss**](https://github.com/leizongmin/js-xss)

* Escape HTML entity characters to prevent the attack which occurs to transform non-readable content for the end users
* npm weekly download 30k+
* 18 contributors
* Earned 2500+ github ⭐️
* 5.3kB MINIFIED + GZIPPED

{% gist https://gist.github.com/DonghyukJacobJang/670347f4f9d05fbb7b8237a37ea0e0f6 %}


[**xss-filters**](https://github.com/yahoo/xss-filters)
* Escape HTML entity characters to prevent the attack which occurs to transform non-readable content for the end users
* npm weekly download 30k+
* 5 contributors
* Earned 900+ github ⭐️
* 2.1kB MINIFIED + GZIPPED

{% gist https://gist.github.com/DonghyukJacobJang/0a970fab31ce42af2f0d6500512ce709 %}


# **Conclusion**

To sum up, finding the most suitable sanitizer library for your project is very important for security. You might want to have a look at GitHub stars, npm download numbers, and maintenance routines. The use of `no-sanitizer-with-danger` in `eslint-plugin-jam3` will be a great choice to help ensure all data is being properly purified and gain confidence that your project will be safe from XSS vulnerabilities.

**NOTE:** Please keep it in mind there is a performance disadvantage of sanitizing data in client-side. For example, sanitizing all data at once may slow down the initial load. To prevent this in large scale web applications, you can implement a "lazy-sanitizing" approach to sanitize on the fly.

# **Further reading and sources**

* ESLint developer guide
* Creating an ESLint Plugin
* eslint-plugin-react
* eslint-plugin-jam3
* Cross-site scripting
* XSS attack cheat sheet

# **Contributors**
* [Donghyuk(Jacob) Jang](https://github.com/DonghyukJacobJang) / [jacob.jang@jam3.com](mailto:jacob.jang@jam3.com)
* [Iran Reyes](https://github.com/iranreyes) / [iran.reyes@jam3.com](mailto:iran.reyes@jam3.com)
* Peter Altamirano / peter.altamirano@jam3.com


![Jam3 logo](https://thepracticaldev.s3.amazonaws.com/i/nge73y4ab5u6vt2p51vu.png)
> Jam3 is a design and experience agency that partners with forward-thinking brands from around the world. To learn more, visit us at [jam3.com](https://jam3.com).


Article by Donghyuk (Jacob) Jang