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:

Explicitly setting href

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

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

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 Reduxarrow-up-right code sample for SSR was vulnerable to XSS (the sample was fixedarrow-up-right):

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:

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