Security Issues

Controlling element type

The createElement function accepts a string in the type argument. If the string is controlled by a user it would be possible to create an arbitrary React component:

// Dynamically create an element from a string stored in the backend
// stored_value is a user-controlled string
element_name = stored_value;
React.createElement(element_name, null);

However, this would result only in a plain, attribute-less HTML element (pretty useless).

Injecting props

The createElement function accepts a list of attributes in the props argument. If the list is controlled by a user it would be possible to inject arbitrary props into the new element:

// Parse user-supplied JSON and pass the resulting object as props
// stored_value is a user-controlled JSON string with attributes
props = JSON.parse(stored_value);
React.createElement("span", props);

You can use the following payload to set the dangerouslySetInnerHTML attribute:

{"dangerouslySetInnerHTML" : { "__html": "<img src=x/ onerror=’alert(localStorage.access_token)’>" }}

Explicitly setting dangerouslySetInnerHTML

If a user-supplied data is used to set the dangerouslySetInnerHTML attribute you can insert arbitrary JavaScript code:

<div dangerouslySetInnerHTML={user_supplied} />

Explicitly setting href

If a user-supplied data is used to set the href attribute you can insert a javascript: URL:

<a href={user_supplied}>Link</a>

Some other attributes such as formaction in HTML5 buttons or HTML5 imports also can be vulnerable:

// HTML5 button
<button form="name" formaction={user_supplied}/>
// HTML5 import
<link rel="import" href={user_supplied}>

Abusing server-side rendering

If a user-controlled data is passed to code that relies on user generated content and input without proper sanitization you can inject arbitrary JavaScript code.

For example, the official Redux code sample for SSR was vulnerable to XSS (the sample was fixed):

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>
    `
}

In the above sample, result of the JSON.stringify function is assigned to a global variable in a <script> tag, this is the vulnerability. If a Redux store has the following value:

{
    user: {
        username: "username",
        bio: "bio</script><script>alert(1)</script>"
    }
}

When a browser parses the page and encounters that <script> tag, it will continue reading until </script>. The browser will not read until the last curly bracket, instead, it will actually finish the script tag after bio: "as.

References:

References

Last updated