Three Common Security Mistakes Every React Developer Should Know About

The author of the article, the translation of which we are publishing today, says that React is her favorite library for creating interactive interfaces. React is both easy to use and fairly well protected. However, this does not mean that React applications are completely invulnerable. It is very easy to fall into unreasonable calm, having decided that you do not have to worry about XSS attacks due to the fact that the project uses React.



Vulnerabilities of React most often occur when the developer thinks that he uses the protective mechanisms of this library, although in fact it turns out that this is not so. Therefore, it is important to correctly assess the capabilities of React, and to know what tasks a programmer needs to solve on his own.







Today we’ll talk about typical React vulnerabilities, how to find them during a code review, and how to defend against them.



First (very short) cross-site scripting example



Cross site scripting (XSS) is a client vulnerability that can lead to serious problems. XSS attacks occur when an attacker is able to trick a website and force it to execute arbitrary JavaScript code in the browsers of its users.



The reflected XSS attack is carried out by means of a link containing text information, which is processed by the browser in the form of code. For example, this is a form field in which, on the client side, a special request text is entered.



A stored XSS attack is a situation in which an attacker has access to the server, and when the code executed on the server generates what gets to the client’s web page. Typical vectors of such attacks are uploading comments and images to servers.



Samy worm exploited the MySSpace XSS vulnerability. It was one of the fastest spreading viruses of all time.



Vulnerable websites may expose their users to theft of passwords or personal data. And this is the usual way to exploit other vulnerabilities. Malicious scripts are most often used to send spam and to redirect users to fraudulent sites. This can damage the reputation and SEO performance of a successfully attacked site.



Vulnerability No. 1: control over the initial state of the page, which is used during server rendering



Sometimes, when we form the initial state of a page, we, which is dangerous, create a document based on a JSON string. This vulnerability in the code looks something like this:



<script>window.__STATE__ = ${JSON.stringify({ data })}</script>
      
      





This is dangerous because the JSON.stringify()



method, without “thinking” of anything, converts any data provided to it in string form (as long as it is valid JSON data), which is what will be displayed on the page. If { data }



has fields that an untrusted user can edit, such as a username or user information, you can embed something like the following into these fields:



 { username: "pwned", bio: "</script><script>alert('XSS Vulnerability!')</script>" }
      
      





This pattern is often used in server-side rendering of React applications that use Redux. He was present in the official Redux documentation, and as a result, many tutorials and sample application templates that can be found on GitHub still use it.



Do not believe? Then see for yourself. Search Google for “react server side rendering example app” and try this attack on any of the search results from the first page.



â–Ť Identification of vulnerability during code review



Look for calls to the JSON.stringify()



method that take variables that may contain untrusted data in the script



tag. Here is an example that used to be in the Redux documentation:



 function renderFullPage(html, preloadedState) {    return `        <!doctype html>        <html>            <head>                <title>Redux Universal 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>        ` }
      
      





And here is a piece of code from the sample application found on GitHub:



 function htmlTemplate( reactDom, reduxState, helmetData ) {    return `    <!DOCTYPE html>    <html>    <head>        <meta charset="utf-8">        ${ helmetData.title.toString( ) }        ${ helmetData.meta.toString( ) }        <title>React SSR</title>        <link rel="stylesheet" type="text/css" href="./styles.css" />    </head>       <body>        <div id="app">${ reactDom }</div>        <script>            window.REDUX_DATA = ${ JSON.stringify( reduxState ) }        </script>        <script src="./app.bundle.js"></script>    </body>    </html>    `;   }
      
      





Sometimes finding this vulnerability is a little more difficult. The following code will also turn out to be unsafe - if the correct escaping of context.data



not performed:



 const RenderedApp = htmlData.replace('{{SSR}}', markup)    .replace('<meta-head/>', headMarkup)    .replace('{{data}}', new ArrayBuffer(JSON.stringify(context.data)).toString('base64'))
      
      





When doing server-side rendering, pay attention to what exactly is being rendered. If what the user enters is not properly shielded and displayed in the DOM, this can be dangerous.



â–Ť Protection



One of the options for protecting against this vulnerability is to use the serialize-javascript



npm module, which is designed to shield the output JSON. If you are doing server rendering in a non-Node.js environment, then you will need to choose the appropriate package for your language.



Here is the command to install the module:



 $ npm install --save serialize-javascript
      
      





After that, you need to import it into a file and rewrite previously vulnerable code dealing with window



follows:



 <script>window.__STATE__ = ${ serialize( data, { isJSON: true } ) }</script>
      
      





Here is a great article on this subject.



Vulnerability â„–2: insidious links



The <a>



tag may have the href



attribute, which contains a link to another page of the site, to another site, to some place on the current page. Links may contain scripts that look something like this: javascript: stuff()



. If you did not know about this HTML feature, try it right now by copying the following code into the browser line:



 data:text/html, <a href="javascript: alert('hello from javascript!')" >click me</a>
      
      





For web developers, this means that if the content of links is set based on data entered by the user, the attacker can add malicious code starting with javascript:



to this data. Then, if the user clicks on a bad link, an attacker script will be launched in the browser.



This vulnerability is definitely not only characteristic of React, but it is one of the problems that React developers often encounter when they expect the corresponding value to be automatically escaped correctly. It should be noted that in a future version of React this problem will be less acute.



â–Ť Identification of vulnerability during code review



Can project users add links to pages that other users can click on? If so, try adding a “link” to the page like the following:



 javascript: alert("You are vulnerable to XSS!")
      
      





If the corresponding message box is displayed when clicking on the link, this means that the project is vulnerable to XSS attacks. Try this wherever you can add links. It is likely that not all such places will be vulnerable.



â–Ť Protection



Protection against this vulnerability is not only suitable for React projects. What exactly needs to be done depends on the application. In addition, you may need to make corrections on the server.



You might think that to solve the problem, it is enough to remove the javascript:



prefix from the data. This is an example of using the blacklist strategy, which cannot be considered successful in cleaning data . Hackers have ingenious ways to bypass such filters, so instead of such a move (or in addition to it), make sure that the links use a whitelisted protocol (for example, http:



and escape HTML entities. Here is a detailed article on this topic regarding the shielding of properties that an attacker can control.



Another strategy that can add an additional level of protection to the project is to use the mechanism for opening custom links in new browser tabs. However, I would not recommend using this strategy as the only "line of defense" of the project. Opening javascript:



links in a new tab is an example of the non-standard behavior of page elements. Most browsers will run the script in an empty tab without harming the user, but this is not guaranteed, and you can probably get around this, which depends on the browser.



Consider using a special UserLink component, which will lead to the fact that the vulnerable <a>



tag is less likely to get to the pages of your project in the future. In addition, it is worth adding a few tests and linting rules to the project, aimed at identifying potentially dangerous code and preventing it from getting into production.



Links are not the only entities that can be used in this way. But they are the most likely attack target in React applications. Any element can be vulnerable to this attack if the attacker can control its URI



value. Another possibility of carrying out this attack, for example, is a view design. A complete list of attributes that may contain URIs can be found in this list using the keyword %URI



using the browser search ( Ctrl+F



).



Vulnerability # 3: misunderstanding the meaning of the construction dangerouslySetInnerHtml



I am extremely grateful to React that the security warning is directly in the method name. This is the name dangerouslySetInnerHTML



. Despite this warning, we are still often confronted with the fact that developers take risks by performing unsafe operations. The same can be said of eval()



.



Consider the following example that I found on the site from the first page of Google search results:



 <script dangerouslySetInnerHTML={{ __html: `window.__PRELOADED_STATE__ = ${JSON.stringify(initialState)};`}}></script>
      
      





This is an example of vulnerability # 1, but with one feature that should immediately draw attention to the fact that something is wrong here. Where I found this, an attempt was made to explain: "We use dangerouslySetInnerHTML as a method of cleaning data and preventing XSS attacks." Well, I do not! It is not right. Do not do so. For more information about dangerouslySetInnerHTML



, read the React documentation.



Another example that this actually happens all the time is how members of one team discovered that they had a vulnerability when they added Markdown markup to a page using dangerouslySetInnerHTML



. In order to protect themselves from this in the future, they began to use a special rule of linting.



â–Ť Identification of vulnerability during code review



Before sending pull requests or performing merge operations, it is useful to search the code for dangerouslySetInnerHTML



and eval



strings (I also look for console.log



commands in this way) or use the corresponding linter rule.



â–Ť Protection



Make sure that in all cases of using the dangerouslySetInnerHTML



method, only data that you can trust is loaded onto the page. How do you know if data can be trusted? If something doesn’t come from you, it can be a threat. This includes data downloaded from external APIs and what is issued using Markdown tools.



Component Spoofing Note



In 2015, someone found out that you can spoof components by passing JSON to those components that expect text. I was able to find only one case of a component spoofing message and a long discussion caused by this message. The discussion focused on what React is responsible for preventing XSS attacks. As a result, React developers released a fix that seems to have helped fix this vulnerability.



I decided not to include a story about this vulnerability in the article, but this topic may be of interest for further research.



SSR Note



The server rendering vulnerability is so widespread due to the fact that it was present in the Redux documentation and, as a result, spread across many other materials. This problem was fixed in 2016. But even today, three years later, beginner guides scattered all over the internet still teach what you shouldn’t teach.



By the way, here's your homework: find an example of this problem on GitHub and send a pull request to fix it. Here is an example .



Together we can once and for all get rid of this vulnerability!



Dear readers! Have you encountered attacks on your React projects?






All Articles