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

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 …

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 …

Tutorial How to use GSAP animation with React

Abstract Hello everyone it’s me candle. In this time we will use GSAP in React. The famous js library which can easily use animation is jQuery. However, it seems that compatibility between jQuery and React is bad. And there are no functions Also, as far as I know, React has no animation function that is easy to use(Yeah, Transition is). But, all times and places, it is rare case that animation is not necessary when you have created a web service. There are many candidates when using animation with React. Of course, you can import only the necessary functions and use …

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. …


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.