読み込み中 ...

2025-06-10
ブログつくるときにちょうどよかったやつです.このブログにも採用しています.
※このブログのコードはここにあります
ユーザがエディタなどで作成したHTMLを画面上に表示することを考えます.
入力として,文字列としてのHTMLがstring型で与えられて,それをパースして表示することを考えます.
イメージとしては,次のようなコードです.
const userContents = () => {
const html = "<h1>Hello World</h1><p>こんにちは</p>"
return <div> {/* ここにhtmlを展開 */}<div>
}
いちばん危険で簡単な方法は,divタグ内でdangerouslySetInnerHTMLを用いて,htmlを表示することですが,XSS攻撃のリスクがあり,使うべきではありません.
特にUGC(User Generated Content)に対しては,ユーザが入力したコンテンツに対するサニタイズ(無毒化)処理は必須です.
安全にユーザが入力したHTMLを表示する際は,次の手順を踏みます.
サニタイズにはDOMPurifyが利用できます.利用例としては次のとおりです.
const sanitizedHtml = DOMPurify.sanitize(html);Reactで表示する場合は,先述したdangerouslySetInnerHTMLを利用することができます.なお,必ずサニタイズしていることを確認する必要があります.
const userContents = () => {
const dangerousHtml = "<h1>Hello World</h1><p>こんにちは</p>"
const sanitizedHtml = DOMPurify.sanitize(dangerousHtml);
const markup = {
__html: sanitizedHtml
}
return <div dangerouslySetInnerHTML={markup}><div>
}もし,dangerouslySetInnerHTMLを利用することに抵抗がある場合は,html-react-parserがおすすめです.
html-react-parserを用いると次のように書けます.
import DOMPurify from "isomorphic-dompurify";
import parse from "html-react-parser";
function contentsElem() {
const dangerousHtml = "<h1>Hello World</h1><p>こんにちは</p>"
const sanitizedHtml = DOMPurify.sanitize(dangeroushtml);
return parse(sanitizedHtml);
}
const userContents = () => {
return <div>{contentsElem}<div>
}tailwindCSSを利用している場合,h1タグやpタグのサイズがだいたい同じくらいのサイズになり,あまりいい感じじゃないです.
taiwindlabs/tailwindcss-typographyを利用すると,このへんのデザインがいい感じになります.
tailwindCSSはclassNameにclassを書き込むことでスタイルが適用できます.tailwindcss-typographyの恩恵を受ける際はproseクラスを付与します.
import DOMPurify from "isomorphic-dompurify";
import parse from "html-react-parser";
function contentsElem() {
const dangerousHtml = "<h1>Hello World</h1><p>こんにちは</p>"
const sanitizedHtml = DOMPurify.sanitize(dangeroushtml);
return parse(sanitizedHtml);
}
const userContents = () => {
return <div className="prose">{contentsElem}<div>
}だいたいいい感じになると思います.
ご指摘などありましたらお気軽にお願いいたします.