joppot

コピペで絶対動く。説明を妥協しない

プログラミング

Remove or allow the html tag with javascript + React and take measures against XSS

投稿日:2018年12月10日 更新日:


Abstract

Hello everyone it’s me candle. In this time, we will write a program which displays only the permitted html tags by React and delete other tags.

Notice, displaying originally html contents, it may has a security risk such as XSS.

First of all, I am not a security expert, so there is a possibility of a bug in the code. Of course, I check it and test it as long as I do. If you find any vulnerabilities in the code, it would be helpful if you point out it in the post comment form.

Condition

Nothing

Preparation of Sample

If you already have React project, please use it. On the other hand, if you don’t have any project, please make sample project with this command.

create-react-app htmltag_sample
cd htmltag_sample

Ready to start.

Implementation

Open the appropriate component file. If you are trying the sample code, open src / App.js.

First of all, write the code to get and display the post of the user from the text area.

import React, { Component } from 'react'

class App extends Component {
  constructor(props) {
    super(props)
    this.state = {
      content: '',
    }
    this.handleFormInputChanged = this.handleFormInputChanged.bind(this)
  }

  handleFormInputChanged(event) {
    this.setState({
      [event.target.name]: event.target.value,
    })
  }
  render() {
    return (
      <div style={{ textAlign: 'center' }}>
        <div>サンプル</div>
        <textarea
          name="content"
          value={this.state.content}
          onChange={this.handleFormInputChanged}
          style={{
            width: '400px',
            height: '300px',
            fontSize: '15px',
          }}
        />
        <div style={{ marginTop: '10px' }}>{this.state.content}</div>
      </div>
    )
  }
}

export default App

The contents written in the text area are displayed.

However as I saw it, the p tag is displayed as it is.
Now we can not display tags so that we use dangerouslySetInnerHTML.
Be careful It has XSS vulnerability.

Change the code. from

<div style={{ marginTop: '10px' }}>{this.state.content}</div>

to

<div dangerouslySetInnerHTML={{ __html: this.state.content }} />

According to this article, it seems that it can disable to XSS of <script> tag.

https://github.com/facebook/react/issues/8838

XSS of <a> or <iframe> is not supported.

Let’s write these code to the text area.

<script>alert("1");</script>

Script tag is no problem.
XSS of a tag will work.

<a onmouseover="alert(1)">XSS</a>

And also iframe tag.

<iframe src="javascript:alert('1');"></iframe>

Remove except specified tag

Create a function that removes the tags other than you specified.

Let’s write this somewhere in the component.

  strip_tags(input, allowed) {
    allowed = (((allowed || '') + '').toLowerCase().match(/<[a-z][a-z0-9]*>/g) || []).join('')
    const tags = /<\/?([a-z][a-z0-9]*)\b[^>]*>/gi
    return input.replace(tags, ($0, $1) => (allowed.indexOf('<' + $1.toLowerCase() + '>') > -1 ? $0 : ''))
  }

Usage is that it requires in the render function and throws to the dangerouslySetInnerHTML.

<div style={{ marginTop: '10px' }}>
  <div dangerouslySetInnerHTML={{ __html: this.strip_tags(this.state.content, '<a>') }} />
</div>

In the above example, a tag is permitted and others are deleted.

Let’s write such contents.

<iframe src="javascript:alert('XSS');">hello</iframe>

<h1>H1 tag</h1>

<a href="https://www.google.com" target="_blank">google</a>

You can see that only permitted tags are displayed. But, this also has some problems. If it is a permitted tag, onEvent and sytle can be executed.

So we will create a function to remove the properties of these tags.

<a onmouseover="alert(document.cookie)">XSS</a>

Remove and allow tag properties

As you saw above, if you only allow tags, malicious users can attack with onmouseover="alert(document.cookie)" or style="font-size: 800px".

Add this function.

  strip_properties(input, allowed) {
    allowed = (((allowed || '') + '').toLowerCase().match(/[a-z][a-z0-9]*/g) || []).join('')
    const properties = /\s([a-z][a-z0-9]*)="[^"]*"/gi
    return input.replace(properties, ($0, $1) => (allowed.indexOf($1.toLowerCase()) > -1 ? $0 : ''))
  }

When using it you do like this.
Since we only allow a tag, we allow href andtarget properties.

<div style={{ marginTop: '10px' }}>
  <div
    dangerouslySetInnerHTML={{
      __html: this.strip_properties(this.strip_tags(this.state.content, '<a>'), ['href', 'target']),
    }}
  />
</div>

It seems no problem to try the XSS test code.

<script>alert("1");</script>
<a onmouseover="alert(1)">XSS</a>
<iframe src="javascript:alert('1');"></iframe>

Completed sample code

At last, I wrote the sample code.

import React, { Component } from 'react'

class App extends Component {
  constructor(props) {
    super(props)
    this.state = {
      content: '',
    }
    this.handleFormInputChanged = this.handleFormInputChanged.bind(this)
  }

  handleFormInputChanged(event) {
    this.setState({
      [event.target.name]: event.target.value,
    })
  }

  strip_properties(input, allowed) {
    allowed = (((allowed || '') + '').toLowerCase().match(/[a-z][a-z0-9]*/g) || []).join('')
    const properties = /\s([a-z][a-z0-9]*)="[^"]*"/gi
    return input.replace(properties, ($0, $1) => (allowed.indexOf($1.toLowerCase()) > -1 ? $0 : ''))
  }

  strip_tags(input, allowed) {
    allowed = (((allowed || '') + '').toLowerCase().match(/<[a-z][a-z0-9]*>/g) || []).join('')
    const tags = /<\/?([a-z][a-z0-9]*)\b[^>]*>/gi
    return input.replace(tags, ($0, $1) => (allowed.indexOf('<' + $1.toLowerCase() + '>') > -1 ? $0 : ''))
  }

  render() {
    return (
      <div style={{ textAlign: 'center' }}>
        <div>サンプル</div>
        <textarea
          name="content"
          value={this.state.content}
          onChange={this.handleFormInputChanged}
          style={{
            width: '400px',
            height: '300px',
            fontSize: '15px',
          }}
        />
        <div style={{ marginTop: '10px' }}>
          <div
            dangerouslySetInnerHTML={{
              __html: this.strip_properties(this.strip_tags(this.state.content, '<a>'), ['href', 'target']),
            }}
          />
        </div>
      </div>
    )
  }
}

export default App

Conclusion

How is it ?
If you have any problems, please report in the comments section.

スポンサードリンク

If you think this article is good, share it please

-プログラミング
-,

執筆者:


comment

Your email address will not be published. Required fields are marked *

関連記事

React Native slows down when debugging on Google Chrome

English 日本語 Abstract Hello everybody, It’s candle. This time, we will solve the problem that using simulator is going to slow down when debugging on the Google Chrome Anyway, It is solved in a simple way. we use Chromium instead of Google Chrome. Premise React development environment has been established

I created docker shortcut command with bash alias

Abstract Hello everyone it’s me candle. I will introduce easy operation of the docker command. Of course, I just created it thinking that it was useful, so please change it for much more useful. Precondition You use bash. ( Even though you use zsh or csh or something else, you edit a bit, it will work well) You use docker

How to change the language form “en-US” to “en” by wordpres bogo

English 日本語 Abstract Hello everyone, It’s me candle. In this time, I will show you to customize bogo plugins. The bogo is wonderful plugin which can adapt the wordpress site to many languages as a simple. but, there is a problem that you can’t choose general English. When writing English articles, you may not always have to write it limited to country. However, you can choose ‘en-UK’, ‘en-CA’ and ‘en-US’, but ‘en’ can not be chosen in bogo. I checked the source code. The bogo got a language list from wordpress function, and there is no general English in it. …

Deploy React to s3 with aws command

Abstract Hello veryone it’s me candle. In this time I will show you how to deploy a react project created by create-react-app to S3. A good thing about React web app is that you can publish the products created by building them as long as you upload them such as html and js files and images to storage. In this article I don’t write these three items. How to get a IAM that can access to S3 Allocate your own domain using Route53 SSL connection of React web app Condition You have aws command Please install the aws command in …

Create the simplest drop down menu in React

Abstract Hello everyone it’s me candle. In this time we will make a most simple drop down menu. Condition Knowledge of react Preparation We create a react project with the below command. create-react-app hello-menu Open src/App.js and write this. import React, { Component } from 'react' class App extends Component { render() { return ( <div> <p>hello menu</p> </div> ) } } export default App Create a components directory in the src directory. mkdir src/components Create a DropDownMenu.js file in the src/components directory. touch src/components/DropDownMenu.js Now on ready. Create a Drop down menu Open the src/components/DropDownMenu.js and write these. import …


I work in the venture company as a CTO. I start to write program in University, first I learned java, C++ and PHP. In the company, I'm developing web services by Rails. I do like to automation.