はじめに
とあるReactアプリの実装でユーザーが入力したHTMLをレンダリングして表示させる部分がありました。
レンダリングの方法としてはdangerouslySetInnerHTML
がありましたが、調べるうちにこの方法にはセキュリティ上の脆弱性が存在していることがわかりました。
ここではdangerouslySetInnerHTML
の危険性と、その問題を回避するために採用したDOMPurify
について書いていきます。
dangerouslySetInnerHTMLとXSSの危険性
ReactでHTMLをレンダリングさせる方法としてはdangerouslySetInnerHTML
が挙げられます。
<div dangerouslySetInnerHTML={( __html: (描画したいHTML) )}>
の形で記述することで、レンダリングしたいHTMLをReact上で表示させることができます。
React公式ドキュメント上のサンプルコード
const markup = { __html: '<p>some raw html</p>' };
return <div dangerouslySetInnerHTML={markup} />;
ただし、dangerouslySetInnerHTML
はHTMLとしてスクリプト入りのコードを記述するとそのスクリプトが実行されてしまうという問題を抱えています。
これを悪用してユーザー情報を抜き取ったりサイト上で意図せぬ動作を引き起こさせたりすることがいわゆるXSS=クロスサイトスクリプティングです。
参考
危険なコードの例
※React公式から引用
例えば以下のようなコードがあり、HTMLにスクリプトが書かれているとします。
const post = {
content: `<img src="" onerror='alert("you were hacked")'>`
};
export default function MarkdownPreview() {
const markup = { __html: post.content };
return <div dangerouslySetInnerHTML={markup} />;
}
この場合、content
に書かれたonerror="alert"
の処理が走ってしまうことになります。
上記の事象を応用して悪意のあるスクリプトを埋め込むことでXSSが引き起こされてまいます。
XSS対策としてのサニタイズとDOMPurify
dangerouslySetInnerHTMLには上記のようにスクリプトがそのまま実行されてしまう脆弱性が存在します。
その対策として必要になってくるのがサニタイズです。
HTMLや属性値などを出力する際、サニタイズ処理をすることで特別な意味を持つメタ文字を無効化し XSSを防ぐことができます。サニタイズ処理をすることで、例えば<→<、>→>のようにタグを無害な文字列に置き換えることができます。
引用元:https://zenn.dev/oreo2990/articles/d33a264b2d8b4c
上記の処理を行うことでHTMLのレンダリング時にスクリプト部分を除去する必要があります。
Reactでこれを行ってくれるライブラリがDOMPurifyです。
導入方法
①インストール
npm install isomorphic-dompurify
でインストールします。
②インポート
使いたいファイル内でimport DOMPurify from 'isomorphic-dompurify';
の形でインポートします。
③対象のHTMLをサニタイズする
HTML要素を囲む外側でDOMPurify.sanitize
を記述します。
DOMPurify.sanitize("記述したいHTML")
の形です。
DOMPurifyを使うことの効果
dangerouslySetInnerHTML単体のコード
まずはXSS対策が行われていないdangerouslySetInnerHTML
のコードを見てみます。
<div
dangerouslySetInnerHTML={{
__html: `<img src="" onerror='alert("you were hacked")'>`,
}}
するとHTMLに入っているアラートが実行されてしまいます。
HTMLを見てみると、onerror
属性として意図せぬスクリプトが混入していることがわかります。
DOMPurifyを併用したコード
次にDOMPurifyを導入したコードを見てみます。
<div
dangerouslySetInnerHTML={{
__html: DOMPurify.sanitize(
`<img src="" onerror='alert("you were hacked")'>`,
),
}}
></div>
出力されたコードを見てみるとアラートが発生せず、該当の部分からonerror属性の記述が削除されていることがわかりました。そのため悪意のあるスクリプトが実行されずに済んだということです。
これらのことから、DOMPurifyを使うことでXSS対策に一定の効果があると言えそうです。
最後に
dangerouslySetInnerHTML
はHTML文字列をレンダリングさせるためには便利な反面、そのままでは脆弱性のリスクが潜んでいます。
DOMPurifyなどのサニタイズ方法と併用することで脆弱性を低減させてHTMLレンダリングをすることが可能になります。
参考資料