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 *

関連記事

Ruby regex that does not match if there are keywords that you don’t want included in the string

Abstract Hello everyone it’s me candle. In this time I would like to create a regular expression that does not pass if keywords that I do not want included in the string match. This is such case like this. For example, the regex matches these words “Bitcoin” and “Bitcoin ~” but doesn’t match “BitcoinCash” and “Bitcoin Cash”. ○ Bitcoin ○ Bitcoin Core × BitcoinCash × Bitcoin Cash Condition Nothing Write the regex This is a regex that does not match if there are keywords that you do not want included in the string. /Keyword to include(?!Keywords you do not want …

Hiragino Sans How to write to change weight in css

Abstract Hello everyone it’s me candle. In this time I will show you how to write css of Hiragino Sans.The feature of Hiragino Sans is to be able to change the weight of font from 1 to 9 levels. On the other hand, there are various ways of writing css of Hiragino Sans and I did not know exactly how to write it, so I wrote a program and test it. It will help you who will use Hiragino Sans. The quick reference is released here. http://hiragino.joppot.info Condition Nothing SPONSORED LINK How to write css of Hiragino Sans From the …

Lazy load image with react-lazyload

Abstract Hello everyoen it’s me candle. This time let’s make a delayed loading of images with react-lazyload. The problem of SPA is the delay at the time of initial loading. Among them, we feel that the site with many images is even late. Let’s try it. Condition Nothing Prepare If you have already developing react project, use it. But you don’t have yet or try to test. Generate it with this command. create-react-app imageloader-sample cd imageloader-sample We will prepare images for samples with Faker.js, so install it. yarn add faker Ready to develop. Make a sample code Open the src/App.js …

Create plantUML with emacs using plantuml-mode

Autumn leaves on wood table Abstract Hello everyone it’s me candle. In this time, we will introduce plantuml-mode of emacs supporting plantUML. plantuml-mode has three functions. Syntax highlight Autocomplete Display UML preview There are plenty of settings, so let’s do it patiently. Precondition You had plantuml command I installed plantuml with brew. but it is ok you prepare jar file. If you don’t have it yet, please refer to here. Build PlantUML environment on Mac using brew Install plantuml-mode The latest version of plant-mode is not yet registered in el-get packages, so we will get it from github directly. For …

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 …


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.