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

React Project development environment building command create-react-app is too convenient

English 日本語 Abstract Hello everyone It’s me candle. In this time we will build a “react” development environment with create-react-app command and try to hello world. Past react development was created by installing babel and other packages from npm on your own using gulp or webpack. Past react development installed babel and other packages from npm and built servers with gulp and webpack. If you use the create-react-app command, you can create react environment easily. Let’s do it. Precondition Node is already installed

Using sphinx how to generate documents of python classes and functions

Abstruct Hello everyone it’s me candle. In this article I will explain how to make documents about classes and functions with Sphinx automatically. condition I will explain it using Python 3, but I think that it will probably work with python 2 as well. Prepare test files Create a main.py and dog.py. touch main.py dog.py Write this to the main.py from dog import Dog def main(): dog = Dog() dog.show_name() if __name__ == "__main__": main() Open the dog.py and write it. class Dog(object): def __init__(self, name="bob"): self.name = name def bark(): print("Bow Wow") def show_name(self): print(self.name) def get_name(self): return self.name …

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 …

4 functions of scikit-learn preprocesses data such as machine learning

Abstract Hello every one this is candle. In this time we will prreprocess a data with scikit-learn which is machine learning library of python. We will use scikit-learn called With scikit-learn you can use what is called a converter, and you can convert the input data with fit_transform () method.Since there are many converters, I will introduce the following four converters that are often used in machine learning. Imputer StandardScaler MinMaxScaler OneHotEncorder Condition Python3 scikit-learn 0.19.1 For running sample code, you need numpy aside from these libs.


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.