どこでも簡単にHTMLのサニタイズ with Svelte
導入
カスタムHTMLのレンダリングはXSSの危険性があるので慎重になるのは言わずもがな。
一般的にフロントエンドでは最強のサニタイズライブラリDOMPurifyを使うのが鉄板ですが、これだけでは色々と不足点が出てくるので色々ライブラリを作りました。
サーバーサイドでもサニタイズしたい
モダンなフロントエンドというかBFFアーキテクチャではSSRが使われますが、先述のDOMPurifyはDOMを使用するのでサーバーでは使用できません。では、どうするかというとJSDOMを使って無理やりサーバー上にDOMを作成し、DOMPurifyを動かす方法があります。それを1パッケージにして使いやすくしたのがuniversal-dompurifyです。
使い方は簡単。
import DOMPurify from 'universal-dompurify'
const cleanedHtml = DOMPurify.sanitize(/* サニタイズしたいHTML */)
通常のDOMPurifyでは、このようなコードを書くとサーバー上でエラーが発生しますが、このライブラリでは何の問題もなくサーバー上でHTMLをサニタイズしてレンダリングできます。
もっと楽したい
私はSvelteが好きなのでSvelte用に単純化したライブラリが欲しくなりました。
Svelte用のDOMPurifyコンポーネントライブラリ
詳細な使い分けはREADMEに譲るとして、最も簡単な使い方を紹介します。
<script>
import { Render } from 'svelte-purify'
const code = '<h1>Hello World</h1>'
</script>
<!-- {@html code}と同等(サニタイズ済み) -->
<Render html={code} config={/* DOMPurify Config (オプション) */} />
しかし…
問題はJSDOMがNode.jsランタイムを必要とすることです。一般的なNodeサーバーなら問題ありませんが、近年はエッジランタイムなどの非Node環境での運用も増えています。これに対処するには主に2つ方法が考えられます。
1つはNodeポリフィルを適用することです。
しかしこれはバンドルサイズの増大やパフォーマンスの悪化、互換性の問題が避けられません。
もう1つはDOMを使用しないサニタイザーを使用することです。有名どころにsanitize-htmlがあるので、今回はこれを使ってIsomorphicなサニタイズライブラリを作成しました。
エッジでのSSRサニタイズ
package.json
のConditional Exportを使用してDOMPurifyとsanitize-htmlを使い分ける。それを1パッケージにして使いやすくしたのがuniversal-sanitizerです。
使い方は簡単。
import { sanitize } from '@jill64/universal-sanitizer'
const cleanedHtml = sanitize(/* サニタイズしたいHTML */, {
// options: {
// sanitizeHtml: sanitize-htmlのオプション
// dompurify: DOMPurifyのオプション
// }
})
universal-dompurifyでは対応できなかった非Node環境でもこのライブラリなら簡単にサーバーサイドでサニタイズできます。ただし、DOMPurifyとsanitize-htmlでは微妙に挙動が異なる点には注意です。詳細は各ライブラリのドキュメントを参照してください。
Svelte用のサニタイズコンポーネント
こちらも同じくSvelte用のライブラリを作りました。
<script>
import { Render } from '@jill64/svelte-sanitize'
</script>
<Render
html={/* サニタイズしたいHTML */}
options={/* {
sanitizeHtml?: sanitize-html options
dompurify?: DOMPurify options
hook?: DOMPurify Hooks function
} */}
/>
まとめ
いかがだったでしょうか。
この記事がSSRやSvelteでHTMLサニタイズの簡素化をしたいと思った方の助けになれば幸いです。
また上記ライブラリ群についてバグや不明点がありましたらぜひ各リポジトリからIssueを開いてください。
Discussion