ユーザー入力値をMarkdownとして受取り、HTMLとしてレンダリングしたい場面は少なくない。
当然ながらHTMLとして出力するため、onerror
やhref
属性などは適切にサニタイズしなければならない。
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>
これでスキーム等も絞れば、たぶん安全にレンダリングできるはず。