General Recommendations for React Secure Coding

Version: 1.0


Introduction

This document summarizes the WSO2 Secure Coding Guidelines that should be followed by WSO2 engineers while developing React based WSO2 products, as well as applications used within the organization.

The purpose of this document is to increase security awareness and make sure the products and the applications developed by WSO2 are inherently secure, by making sure security best practices are followed throughout the Software Development Life Cycle.

Data Binding

Untrusted user inputs must be handled by the default data binding of React curly brackets. As this binding feature of React can neutralize the HTML tags by default. So malicious user inputs such as the XSS script will not be executed on the client side1.

Example Correct Usage

function Wso2(){
  Const input="hello <img src=x onerror=alert(0)>";
  Return (<dev>{input}</dev>);}

The above-mentioned protection only occurs when rendering user input data as text content. Likewise, this security measurement could be bypassed when rendering an untrusted user input as an HTML attribute.

Example Incorrect Usage

function wso2(){
  Const input="data:text/html,<script>alert(1)</script>";
  Return (<iframe src={input}></iframe>);
}

To avoid these unwanted malicious script execution, all untrusted user input data must be sanitized before they are rendered as HTML attributes.

Example Correct Usage

Import purify from dompurify;

function wso2(){
  Const input="data:text/html,<script>alert(1)</script>";
  Return (<iframe src="DOMPurify.sanitize(input)"></iframe>);
}

URL Handling

URLs can also contain dynamic scripts via javascript protocol urls. It is highly recommended to validate the URLs before using them. When validating a URL checked whether that link has HTTP or HTTPS components to avoid Javascript-based script injection2.

Example Incorrect Usage

// Classic XSS via anchor tag href attribute.
<a href="javascript: alert(1)">Click me!</a>

Example Correct Usage

isSafe(dangerousURL, text) {
  const url = URL(dangerousURL, {})
  if (url.protocol === 'http:') return true
  if (url.protocol === 'https:') return true    

  return false
}

HTML Rendering

It is possible to insert HTML tags directly into rendered DOM nodes using the dangerouslySetInnerHTML attribute of the DOM element. If required, use this attribute in the wso2 product and with must be contained properly with sanitization3.

Example Incorrect Usage

function wso2(){
  Const input="hello <img src=x onerror=alert(0)>";
  Return (<dev dangerouslySetInnerHTML={{_html:input}}></dev>);
}

To sanitize the input, you can use a sanitization library like DOMPurify4 on any values before placing them into dangerouslySetInnerHTML.

Example Correct Usage

import purify from "dompurify";

function wso2(){
  Const input="hello <img src=x onerror=alert(0)>";
  Return (<dev dangerouslySetInnerHTML={{_html:DOMPurify.sanitize(input)}}></dev>);
}

Direct DOM Access

Avoid accessing DOM to insert the content into DOM nodes directly. It is highly recommended to apply proper sanitization when findDomNode() and createRef access rendered DOM elements directly to inject content via innerHTML and similar properties.

Example Incorrect Usage

class App extends React.Component {   
  changeMe(){
    let input = "<img src=x onerror=alert(0)>";
    ReactDOM.findDOMNode(document.getElementById("wso2")).innerHTML= input;
  }

  render() {
    return (
      <dev>
        <button onClick={this.changeMe}>change me</button><br></br>
        <dev id="wso2">Welcome to XSS Test</dev>
      </dev>
    )
  }
}

Example Correct Usage

class App extends React.Component {   
  changeMe(){
    let input = "<img src=x onerror=alert(0)>";  ReactDOM.findDOMNode(document.getElementById("wso2")).innerHTML=DOMPurify.sanitize(input);
  }

  render()
  {
    return (
      <dev>
        <button onClick={this.changeMe}>change me</button><br></br>
        <dev id="wso2">Welcome to XSS Test</dev>
      </dev>
    )
  }
}

Server Side Rendering

JSON

Sometimes when rendering the initial state on the server side, that dangerously generates a document variable from a JSON string. This is risky because JSON.stringify() will blindly turn any data that you give it into a string (as long as it is a valid JSON), which will be re-rendered on the page. If fields that untrusted users edit and inject malicious scripts.

Example Incorrect Usage

<script>
  window._PRELOADED_STATE_ = ${JSON.stringify(untrusted_JSON_data)}
</script>

To fix this vulnerability when serializing the state on the server to be sent to the client, it must be serialized in a way that sanitizes the HTML tags. It is highly recommended to use a library like serialize-javascript5 to avoid unnecessary HTML tag renders6.

Example Correct Usage

import purify from "dompurify";

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

Third-Party Dependencies

Before using any third-party npm packages or libraries on WSO2 products, ensure that the above-mentioned vulnerable code components are not used in those dependencies. Also, these third-party dependencies must not contain any known vulnerabilities. To detect vulnerabilities in third-party components, use tools like npm audit7.

References