March 24, 2018

Reactで安全にMarkdownをHTMLレンダリングする

ユーザー入力値をMarkdownとして受取り、HTMLとしてレンダリングしたい場面は少なくない。
当然ながらHTMLとして出力するため、onerrorhref属性などは適切にサニタイズしなければならない。

import React, { Component } from 'react';
import marked from 'marked';
import PropTypes from 'prop-types';

const markdown = (data) => {
  return marked(data)
}

const SanitizedOutput = (props) => {
  const { data } = props;
  return (<div dangerouslySetInnerHTML={{__html: markdown(data)}}></div>)
}

SanitizedOutput.propTypes = {
  data: PropTypes.string.isRequired
}

export default SanitizedOutput;

このとき、data<img src='x' onerror='alert(1)'>だと当然ながらXSSが生じる。

<div>
    <img src='x' onerror='alert(1)'>
</div>

sanitized-htmlを使えば柔軟に使用できるタグや属性を指定できる。
例えば、imgタグを許可し、imgタグのsrc属性のみを許可したい時は以下のようになる。

import React, { Component } from 'react';
import marked from 'marked';
import sanitize from 'sanitize-html';
import PropTypes from 'prop-types';

const markdown = (data) => {
  return sanitize(marked(data), {
    allowedTags: sanitize.defaults.allowedTags.concat([
          'img'
        ])
  })
}

const SanitizedOutput = (props) => {
  const { data } = props;
  return (<div dangerouslySetInnerHTML={{__html: markdown(data)}}></div>)
}

SanitizedOutput.propTypes = {
  data: PropTypes.string.isRequired
}

export default SanitizedOutput;
<div>
    <img src='x'>
</div>

これでたぶん安全にレンダリングできるはず。


このエントリーをはてなブックマークに追加

© Kouhei Morita 2018