This is the 70th original article, want to get more original good articles, please search the public account to follow us ~ this article first appeared in the politics adopt cloud front end blog: Talk about the React XSS attack

preface

Front-end usually faces security risks like XSS, but with the popularity of modern front-end frameworks like React, we don’t have to pay much attention to security issues in daily development. Take React as an example. React is designed to be a good defense against XSS. This article takes a look at what React does to achieve this security from a source perspective.

What is an XSS attack

Cross-site Scripting (XSS) is a code injection attack. An XSS attack usually refers to exploiting vulnerabilities in a web page. An attacker injects XSS code into a web page in a clever way, because the browser cannot tell which scripts are trusted, causing XSS scripts to be executed. XSS scripts can often steal user data and send it to an attacker’s web site, or impersonate a user to invoke the target web site interface and perform the operations specified by the attacker.

XSS attack type

Reflective XSS

  • The XSS script comes from the current HTTP request
  • When the server receives data in an HTTP request and returns the data concatenated in HTML, example:
   // A website has a search function, which receives the search terms provided by the user through the URL parameter:
   https://xxx.com/search?query=123
   // The server responds to this URL with the search terms provided:
   <p>You searched for: 123</p>
   // If the server does not escape the data, the attacker can construct the following links to attack:
   https://xxx.com/search?query=<img src="empty.png" onerror ="alert('xss')">
   // This URL will result in the following response and run alert(' XSS ') :
   <p>You are searching for:<img src="empty.png" onerror ="alert('xss')"></p>// If a user requests the attacker's URL, the script provided by the attacker will be executed in the user's browser.Copy the code

Type stored XSS

  • The XSS script comes from the server database
  • The attacker submits the malicious code to the database of the target website. When ordinary users visit the website, the server will return the malicious code and the browser will execute it by default. For example:
// A comment page where you can view user comments. // The attacker submitted the malicious code as a comment, and the server did not escape the data. // Comment input:<textarea>
      <img src="empty.png" onerror ="alert('xss')">
   </textarea>// Then the script provided by the attacker will be executed in the browsers of all users accessing the comment pageCopy the code

The DOM model XSS

The vulnerability exists in the client code, not the server

  • Similar to reflection XSS, the difference is that DOM XSS does not interact with the background, and the front end directly does not process the data in the URL and dynamically inserts it into HTML, which is a pure front end security problem, and can only be defended on the client side.

How does React prevent XSS attacks

Whatever attack is used, the essence of the attack is to inject malicious code into the application, and the browser executes it by default. React DOM escapes all input by default before rendering it. It ensures that you never inject content into your application that you didn’t explicitly write. All content is converted to strings before rendering, so malicious code cannot be injected successfully, effectively preventing XSS attacks. Let’s look at it in detail:

Automatically escape

React escapes the characters “‘&<> when rendering HTML content and when rendering DOM properties.

for (index = match.index; index < str.length; index++) {
    switch (str.charCodeAt(index)) {
      case 34: // "
        escape = '" ';
        break;
      case 38: / / &
        escape = '& ';
        break;
      case 39: / / '
        escape = '' ';
        break;
      case 60: // <
        escape = '< ';
        break;
      case 62: // >
        escape = '> ';
        break;
      default:
        continue; }}Copy the code

This code is escaped by React before rendering to the browser. You can see that all characters with special meaning to the browser are escaped. The malicious code is converted to strings before rendering to HTML, as follows:

// Malicious code
<img src="empty.png" onerror ="alert('xss')"> 
// Output to HTML after escaping&lt; img src=&quot; empty.png&quot; onerror =&quot; alert(&#x27; xss&#x27;) &quot; &gt;Copy the code

This effectively prevents XSS attacks.

JSX grammar

JSX is actually syntax sugar. Babel compiles JSX into a function call called React. CreateElement (), which returns a ReactElement.

// JSX
const element = (
  <h1 className="greeting">
      Hello, world!
  </h1>
);
// The code compiled with Babel
const element = React.createElement(
  'h1',
  {className: 'greeting'},
  'Hello, world! '
);
// ReactElement returned by the React. CreateElement () method
const element = {
  $$typeof: Symbol('react.element'),
  type: 'h1'.key: null.props: {
    children: 'Hello, world! '.className: 'greeting'}... }Copy the code

As you can see, the final render is in the Children property. Now that we know how JSX works, let’s see if we can construct special Children for XSS injection. Look at the following code:

const storedData = `{ "ref":null, "type":"body", "props":{ "dangerouslySetInnerHTML":{ "__html":"" } } }`;
/ / to JSON
const parsedData = JSON.parse(storedData);
// Render the data to the page
render () {
    return <span> {parsedData} </span>; 
}
Copy the code

In this code, the following error is reported, indicating that the ReactChild is not valid.

Uncaught (in promise) Error: Objects are not valid as a React child (found: object with keys {ref, type, props}). If you meant to render a collection of children, use an array instead.
Copy the code

So what went wrong? Let’s look at the source code for ReactElement:

const symbolFor = Symbol.for;
REACT_ELEMENT_TYPE = symbolFor('react.element');
const ReactElement = function(type, key, ref, self, source, owner, props) {
  const element = {
    // This tag uniquely identifies this as a ReactElement
    $$typeof: REACT_ELEMENT_TYPE,
    // The built-in attributes of the element
    type: type,
    key: key,
    ref: ref,
    props: props,
    // Record the component that created this element
    _owner: owner, }; .return element;
}
Copy the code

Note that there is an attribute called $$Typeof, which is used to indicate that the object is a ReactElement. React will check this attribute before rendering. If it fails, it will throw the error above. React uses this property to prevent XSS attacks by constructing special Children. The reason is that $$Typeof is a Symbol type. After JSON conversion, the Symbol value will be lost and cannot be transmitted in front and back ends. If the user submits special Children, they cannot be rendered. This feature can be used to prevent stored XSS attacks.

Some of the writing that can cause bugs in React

Using dangerouslySetInnerHTML

DangerouslySetInnerHTML is React’s alternative to the innerHTML provided by the browser DOM. In general, using code to set HTML directly is risky, because it’s easy to expose users to XSS attacks, because when using dangerouslySetInnerHTML, React will do nothing with the input and render it directly into HTML, If an attacker passes malicious code in dangerously lysetinnerHTML, then the browser will run the malicious code. Take a look at the source:

function getNonChildrenInnerMarkup(props) {
  const innerHTML = props.dangerouslySetInnerHTML; // There is a dangerouslySetInnerHTML attribute that renders __html content without escaping it
  if(innerHTML ! =null) {
    if(innerHTML.__html ! =null) {
      returninnerHTML.__html; }}else {
    const content = props.children;
    if (typeof content === 'string' || typeof content === 'number') {
      returnescapeTextForBrowser(content); }}return null;
}
Copy the code

So usually development had better avoid using dangerouslySetInnerHTML, if you have to use, front-end or server must be relevant to the input verification, such as filtering, escape for special input processing. For the front end, it is recommended to use whitelist filtering to control the allowed HTML tags and the attributes of each tag.

Create React components from user-provided objects

Here’s an example:

// User input
const userProvidePropsString = `{"dangerouslySetInnerHTML":{"__html":""}}"`;
// After the JSON conversion
const userProvideProps = JSON.parse(userProvidePropsString);
// userProvideProps = {
// dangerouslySetInnerHTML: {
// "__html": ``
/ /}
// };
render() {
     // Parses the user-supplied JSON for some reason and passes the object as props
    return <div {. userProvideProps} / > 
}
Copy the code

When a user constructs a special string like the one in this example, the page will be injected with malicious code, so be careful not to use user input as the property directly in your development.

The user input is used to render the href attribute of the A tag, or the SRC attribute like the IMG tag

const userWebsite = "javascript:alert('xss');";
<a href={userWebsite}></a>
Copy the code

If the URL is not filtered to prevent javascript from being executed through javascript: or data:, then an attacker can construct an XSS attack, where there are potential security issues. The URL provided by the user needs to be authenticated and filtered by the front-end or server before being stored.

How does the server prevent XSS attacks

The server, as the last line of defense, also needs to do some measures to prevent XSS attacks, generally involving the following aspects:

  • When user input is received, it needs to be filtered as strictly as possible, filtering or removing special HTML tags, JS event keywords, and so on.
  • The data is escaped during output, and the corresponding escape is carried out according to the output context (HTML /javascript/ CSS/URL)
  • If you set the http-only attribute to a key Cookie, the JS script cannot access the http-only Cookie
  • Using CSP to defend against or mitigate XSS attacks, a CSP-compliant browser will only execute script files obtained from whitelist fields, ignoring all other scripts (including inline scripts and HTML event handling properties).

conclusion

XSS vulnerabilities occur because of inadequate input/output validation. React is designed to be secure, but some anti-pattern writing can still cause security vulnerabilities. Similar to Vue, Vue’s security measures are mainly escaping, HTML content and dynamically bound attributes will be escaped. No matter React or Vue or other front-end frameworks are used, there is no 100% protection against XSS attacks, so the server must do some validation of front-end parameters, including but not limited to special character escape, label, attribute whitelist filtering, etc. Once a security problem occurs, it is usually quite serious, whether sensitive data is stolen or user funds are stolen, the loss is often irreparable. We usually need to maintain security awareness in development, to maintain the reliability and security of the code.

Little game

After reading the article, you can try XSS small game, hands-on practice simulation XSS attack, you can have a further understanding of XSS.

My front – end career path!

A list of Git exception handlers worth keeping

, recruiting

ZooTeam (ZooTeam), a young and creative team, belongs to the product RESEARCH and development department of ZooTeam, based in picturesque Hangzhou. The team now has more than 50 front end partners, the average age of 27 years old, nearly 30% are full stack engineers, no problem youth storm team. The membership consists of “old” soldiers from Alibaba and netease, as well as fresh graduates from Zhejiang University, University of Science and Technology of China, Hangzhou Electric And other universities. In addition to the daily business connection, the team also carried out technical exploration and actual practice in the direction of material system, engineering platform, building platform, performance experience, cloud application, data analysis and visualization, promoted and implemented a series of internal technical products, and continued to explore the new boundary of the front-end technology system.

If you want to change the things you’ve been doing, you want to start doing things. If you want to change, you’ve been told you need to think more, but you can’t change; If you want to change, you have the power to achieve that result, but you are not needed; If you want to change what you want to accomplish, you need a team to support you, but there is no place for you to bring people; If you want a change of pace, it’s “3 years of experience in 5 years”; If you want to change the original savvy is good, but there is always a layer of fuzzy window paper… If you believe in the power of belief, that ordinary people can achieve extraordinary things, that you can meet a better version of yourself. If you want to get involved in the growth of a front end team with a deep understanding of the business, a sound technology system, technology that creates value, and spillover impact as the business takes off, I think we should talk about it. Any time, waiting for you to write something, to [email protected]