primers

Some time ago, the department has been recruiting front-end development in order to make our website

  • faster

  • A more stable

  • A more secure

    I also briefly asked candidates about front-end security issues, including knowledge of XSS attacks. Here is our interview process.

    Me: XXX do you know anything about XSS attacks?

    Candidate: XSS knows, just… But I feel like we don’t need to do XSS protection anymore with modern frameworks like React. The framework already does this for us.

    Me: Does using a framework like React guarantee that XSS attacks won’t happen?

At this point the candidates began to falter. Readers, silently ask yourself: Does react really prevent XSS attacks? In order to make you better grasp the knowledge related to XSS attack, the author will mainly focus on the following points to you.

  • What is an XSS attack
  • Example of XSS attack
  • XSS attacks in the React framework
  • XSS attack defense
  • conclusion

What is an XSS attack

Cascading Style Sheets (CSS). XSS is a computer security vulnerability in Web applications. It allows malicious Web users to embed code into pages that are intended for use by other users.

XSS attack classification

  • reflective

    Reflective XSS simply “reflects” user input data back to the browser. In other words, hackers often need to trick users into “clicking” on a malicious link in order to succeed. Reflective XSS is also called “non-persistent XSS”.

  • Storage type

    Storage XSS “stores” user-entered data on the server side (the database). Storage XSS is often called Persistent XSS.

  • Dom Based XSS

    Dom Based XSS is not divided according to “whether data exists on the server side”. Dom Based XSS is also reflective XSS in effect. It is separate because DOM Based XSS is formed for a special reason — XSS formed by modifying the DOM node of the page.

Example of XSS attack

Now that we are familiar with what an XSS attack is and the classification of an XSS attack, let’s be a hacker! Next, we will simulate each of the three types of attacks. I recommend you clone my sample code first.

The code used in this article is in the repository, and the directory is Security/XSS

Running instance

git clone https://github.com/chenshengshui/Web-Frontend-Study-Map.git
Copy the code
cd security
Copy the code
npm install
Copy the code
npm run xss
Copy the code

Example of reflective XSS

Example demonstrates

Open a browser and enter the threat website: http://localhost:3000/non-persistent-xss.html

The source code parsing

The target site prints the parameters entered by the user directly to the page:

/* * Target website */
const express = require('express');
const app = express();

app.use(express.static('./xss'));
app.get('/'.function(req, res) {
  res.setHeader('X-XSS-Protection'.0); // This is done to turn off XSS protection in modern browsers
  res.send('Good morning' + req.query['name']);
});

app.listen(3000);
Copy the code

Threatening websites:


      
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="Width = device - width, initial - scale = 1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>Document</title>
  </head>

  <body>
    <a href="http://localhost:3000/? name=''"
      >Click me to go to the target website</a
    >
  </body>
</html>
Copy the code

The threat site induces the user to click on the link, but the link contains the attack script, and if we go from the threat site to our target site, we will find that the alert(‘ XSS ‘) is executed on the target site. A look at the source code shows that the script tag in the parameter has been written to the page, which is clearly not what the developers want to see.

Storage XSS example

A classic scenario for a stored XSS attack is when a hacker writes a blog post that contains malicious Javascript code. After the post is published, all users accessing the blog post will execute the malicious Javascript code in their browsers. Malicious scripts are saved to the server, so this TYPE of XSS attack is called “stored XSS”.

Example demonstrates

Open a browser, enter the web site: http://localhost:3000/persistent-xss.html, in the text box, enter the following content:

<div style="color: red">Welcome to my blog, I'm WaterMan, follow me if you like!</div>
<script>
  alert('Haha, I stole your token, be careful next time.' + document.cookie);
</script>
Copy the code

After clicking Save, our cookie will be stolen.

Source code analysis first look at our front-end code, So easy, there is only a Textarea and a submit button, click the submit button will be the user input content submitted to the background, no problem, we usually is So developed.

document.getElementById('submit').onclick = function() {
  var content = document.getElementById('textarea').value;
  var url = 'http://localhost:3000/postArticle';
  easyRequest()
    .post(url, {
      content: content
    })
    .then(res= > {
      alert(res.message);
      window.open('http://localhost:3000/persistent-xss');
    });
};

function easyRequest() {
  return {
    post: function(url, data) {
      return fetch(url, {
        body: JSON.stringify(data),
        cache: 'no-cache'.credentials: 'same-origin'.headers: {
          'content-type': 'application/json'
        },
        method: 'POST'.mode: 'cors'.redirect: 'follow'.referrer: 'no-referrer'
      }).then(response= >response.json()); }}; }Copy the code

Looking at our back-end code, the back-end code is also extremely simple, saving our commit to a file, which is used to simulate a regular database.

const express = require('express');
const bodyParser = require('body-parser');
const app = express();
const fs = require('fs');

app.use(express.static('./xss'));
// parse application/x-www-form-urlencoded
app.use(bodyParser.urlencoded({ extended: false }));
// parse application/json
app.use(bodyParser.json());

app.use(function(req, res, next) {
  // Simulate setting user tookie
  res.setHeader('Content-Type'.'text/html; charset=utf-8');
  res.cookie('token'.'zkskskdngqkkkgn245tkdkgj');
  next();
});
app.get('/'.function(req, res) {
  res.setHeader('X-XSS-Protection'.0);
  res.send('Good morning' + req.query['name']);
});

app.get('/persistent-xss'.function(req, res) {
  fs.readFile('./xss/data.txt'.function(err, data) {
    if (err) {
      return res.send(err);
    }
    res.send(data);
  });
});

app.post('/postArticle'.function(req, res) {
  const content = req.body.content + '\n';
  fs.writeFile('./xss/data.txt', content, { flag: 'w' }, function(err) {
    if (err) {
      return res.send({ status: 500.message: err.message });
    }
    res.send({ status: 200.message: 'Saved successfully' });
  });
});

app.listen(3000);
Copy the code

In this way, as long as any one user access article details http://localhost:3000/persistent-xss, his tooken will be obtained.

DOM Based XSS

XSS formed by modifying the DOM node of the page is called DOM Based XSS. Look directly at the following code

<body>
  <div id="content"></div>
  <input type="text" id="text" value="" style="width: 500px" />
  <input type="button" id="submit" value="write" />
</body>
<script>
  document.getElementById('submit').onclick = function() {
    var str = document.getElementById('text').value;
    document.getElementById('content').innerHTML =
      "<a href='" + str + "'>testLink</a>";
  };
</script>
Copy the code

When you click the write button, a hyperlink is inserted into the current page with the address of the contents of the text box. In this case, the “write” button’s onclick event modifies the PAGE’s DOM node, using innerHTML to write a piece of user data into the page as HTML. This creates DOM Based XSS. Construct the following data

‘onclick=alert(/ XSS /) //

<a href=' ' onclick=alert(/xss/)//'> testLink</a>
Copy the code

Close the first href single quote with a single quote, insert an onclik event, and comment out the second single quote. Click the newly generated link and the script will be executed. So we launched a DOM Based XSS attack. You can also start the project, open the web site http://localhost:3000/dom-based-xss.html experience.

XSS attacks in the React framework

The React framework is actually a lot safer than writing code using Jquery, because React already does a lot of work for us. Here’s a quick overview of what React does.

React:

It is safe to embed user input in JSX:

const title = response.potentiallyMaliciousInput;
// This is safe:
const element = <h1>{title}</h1>;
Copy the code

By default, React DOM escapes any values embedded in JSX before rendering them. Thus it ensures that you can never inject anything That’s not explicitly written in your application. Everything is in line to a string before being rendered. This helps prevent XSS (cross-site-scripting) attacks.

Embedding user input directly in React is safe because the React Dom encodes values in JSX before rendering. This ensures that you don’t inject any code that threatens your application. Everything is converted to a string before rendering, which helps defend against XSS attacks.

So how does this HTML entity code work?

& becomes &amp;
< becomes &lt;
> becomes &gt;
" becomes &quot;
' becomes &#39
Copy the code

React doesn’t convert everything all at once, otherwise we wouldn’t be able to render properlyThe defense mechanism of XSS-Fitler is described in more detail here.

Let’s go back to the React XSS attack. React API dangerouslySetInnerHTML is used to render HTML.

DangerouslySetInnerHTML is React’s replacement for using innerHTML in the browser dom. in general, Setting HTML from code is risky because it’s easy to expose your users to a cross-site scripting (XSS) attack. So, you can set HTML directly from React, but you have to type out dangerouslySetInnerHTML and pass an object with a __html key, To remind yourself that it’s dangerous. For example:

function createMarkup() {
  return { __html: 'First &middot; Second' };
}

function MyComponent() {
  return <div dangerouslySetInnerHTML={createMarkup()} />;
}
Copy the code

That said, dangerouslySetInnerHTML is a replacement for innerHtml, but it still has security issues.

Run the example

/security/react-xss

cd security/react-xss
Copy the code
npm install
Copy the code
npm start
Copy the code

Source code analysis

class Chat extends Component {
  state = {
    input: ' '.result: ' '
  };

  onChange = e= > {
    this.setState({
      input: e.target.value
    });
  };

  onSubmit = (a)= > {
    this.setState({
      result: this.state.input
    });
  };

  render() {
    return(<div styleName="container"> <div styleName=" MSG "> <textarea onChange={this.onChange} placeholder=" Input here "/> <button OnClick ={this.onsubmit}> Submit </button> <div dangerouslySetInnerHTML={{__html: this.state.result}} /> </div> </div>); }}Copy the code

When dangerouslySetInnerHTML is used in React, XSS attacks may occur.

React is not foolproof. How can we improve the security of our website? That brings me to the focus of this article, where I’ll cover some of the tools you can use to protect against XSS attacks and make your site more secure.

XSS attack defense

XSS defense is complex. Here are a few of the XSS defenses I’ve learned, and if there are others I haven’t mentioned, please feel free to add in the comments section. Our goal is to make our site more secure and enhance your capabilities.

Next, XSS attack protection is mainly discussed around the following points:

  • Four two money – HttpOnly
  • Input inspection
  • Output check
  • Handle rich text correctly

Four two money – HttpOnly

What is HttpOnly?

HttpOnly is a property that sets whether cookies can be read by javasript scripts. The browser will prevent the page’s Javascript from accessing cookies with the HttpOnly property.

Returning to the example code, look at the following syntax for setting cookies:

const express = require('express');
const app = express();
app.use(function(req, res, next) {
  res.setHeader('Content-Type'.'text/html; charset=utf-8');
  // Simulate setting user tookie
  res.cookie('token'.'zkskskdngqkkkgn245tkdkgj');
  next();
});
Copy the code

The cookie is not set to true, so we can use document.cookie directly to obtain the token value. The following figure

Cookie details can be viewed in the Application TAB of the console

To prevent hackers from accessing cookie information through Javascript scripts, HttpOnly can be enabled.

const express = require('express');
const app = express();
app.use(function(req, res, next) {
  res.setHeader('Content-Type'.'text/html; charset=utf-8');
  // Simulate setting user tookie
  res.cookie('token'.'zkskskdngqkkkgn245tkdkgj', { httpOnly: true });
  next();
});
Copy the code

Native NodeJS can be set up like this without using frameworks such as Express

response.setHeader('Set-Cookie'.'token=zkskskdngqkkkgn245tkdkgj; HttpOnly');
Copy the code

Strictly speaking, HttpOnly is not designed to counter XSS; HttpOnly addresses post-XSS cookiehijacking attacks.

Cookie set HttpOnly, Secure property can effectively prevent XSS attacks, x-frame-options response header can avoid click hijacking.

Properties:

  • Secure When this parameter is set to true, the created Cookie is transmitted to the server in a Secure manner (SSL). That is, the Cookie can only be transmitted to the server in an HTTPS connection for session authentication, but not in an HTTP connection. So you can’t steal the specific contents of the Cookie.

  • HttpOnly Attribute If the HttpOnly attribute is set in cookies, programs (JS scripts, applets, etc.) cannot read Cookie information. In this way, XSS attacks are effectively prevented.

  • X-frame-options Response header X-frame-options Http response header indicates whether a page is allowed to display tags in < Frame >,

/ / set the cookie
response.setHeader(
  'Set-Cookie'.'JSESSIONID=' + sessionid + '; Secure; HttpOnly'
); / / set the Secure; HttpOnly
response.setHeader('x-frame-options'.'SAMEORIGIN'); / / set x - frame - the options
Copy the code

Input inspection

Common Web vulnerabilities, such as XSS and SQL Injection, require the attacker to construct some special characters. These special characters may not be used by normal users, so the existence of input check is necessary.

Input check, often used for format check, simply means adding a whitelist to the information entered by the user. For example, the user name required to fill in the website registration will be required as a combination of letters and numbers. For example, “waterMan1” is a valid username, while “waterMan$^” is not allowed. Input checking must be implemented in server-side code because it can be easily circumvented by attackers using javascript on the client side. That as a front end of us, is it ok to put both hands into the pocket? In the current era of NodeJS, this is often simplistic. We can perform input checks on the Node side to make sure our site is secure. To say the least, we add input check in the client code, can also block most of the normal users who misoperate, so as to save server resources, but also can improve the threshold of hacker attack.

So how do we do an input check?

Rough practice

The most effective way to check input is to filter or encode special characters such as

& becomes &amp;
< becomes &lt;
> becomes &gt;
" becomes &quot;
' becomes &#39
Copy the code

However, due to the lack of context, such violent processing, which is indeed safe, can also make some normal input difficult to understand, and make the user crazy.

Such as:

1 + 2 < 4 becomes 1 + 2 &lt 4
Copy the code

This is obviously not what users want.

More intelligent XSS-filter

XSS Filter obtains variables when users submit data, performs XSS check, and performs intelligent input check to match XSS features. It is very difficult to design such an intelligent XSS Filter library, and we will describe the implementation of an XSS-filter library in detail in the next article. Welcome to witness the process of an XSS-filter library from scratch with me.

Output check

Output checking and input checking do the same thing, but output checking is simpler. Output checking can be context-specific, but output checking requires a lot of work. The React framework already does a lot of output checking. In fact, the output check can also be uniformly intercepted at the interface, so as to be consistent with the input check.

Processing rich text

Processing rich text is the same as input checking. We add a whitelist to the input information, such as tags in rich text, but

conclusion

It’s the end again, and I’m glad you guys made it to the end. XSS defense is worthy of further study. In a word, I hope we can learn from my article. Finally, have a great weekend!

@Author: WaterMan