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 *

関連記事

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 …

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 …

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.

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

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 …


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.