React開発の「安全神話」を解剖する:dangerouslySetInnerHTMLという名のパンドラの箱
こんにちは。現場で泥臭いインシデント対応を繰り返していると、「最新のフレームワークを使っているからうちは安全だ」と信じ込んでいる開発現場に遭遇することがあります。
Reactは確かに優秀です。しかし、どれほど高性能なセキュリティシステムも、設置する人間が「裏口」を全開にしてしまえば無力です。今日は、Reactが標準で備えている「自動防犯システム」と、それを無効化してしまう危険な禁じ手について、身近な例えを交えて紐解いていきましょう。
—
1. Reactの「自動エスケープ」は、優秀な門番
Reactがデフォルトで行うエスケープ処理は、いわば「手紙の検閲官」です。
私たちがReactで {userInput} のように変数を埋め込むとき、Reactは中身に悪意のあるコード(タグなど)が含まれていないかチェックします。もし含まれていれば、それらを単なる「ただの文字」に変換して表示します。
例えば、攻撃者が入力欄に と書き込んだとしても、Reactはこれを「画像を表示しようとする命令」として解釈せず、「 という文字列」として画面に描き出します。これにより、ブラウザは「これは文字だ」と判断し、勝手にスクリプトを実行することはありません。
これが、クロスサイトスクリプティング(XSS)に対するReactの基本的な防御策です。家の入り口に「不審物は持ち込ませない」という頑丈な門番を立たせている状態ですね。
---
2. dangerouslySetInnerHTML は「門番を解雇する」行為
しかし、開発を進めていると「どうしてもHTMLタグを含んだ文字列をそのまま描画したい」という場面に出くわすことがあります。CMSから取得したリッチテキストや、特定のフォーマット済みデータなどがそうですね。
ここで登場するのが dangerouslySetInnerHTML です。
名前の通り「危険を承知でHTMLをセットする」ためのプロパティです。これを使うと、Reactの門番はこう言います。
「君が直接HTMLを流し込むなら、僕はもうチェックしないよ。あとは自分で何とかしてね」
もし、このプロパティにユーザーからの未検証な入力をそのまま渡してしまったらどうなるでしょうか? それは、家の鍵をかけたまま、泥棒に合鍵を渡して「勝手に入っていいよ」と言うのと同じです。攻撃者はその隙に、セッションクッキーを盗んだり、ユーザーを偽サイトへ誘導したりと、やりたい放題できてしまいます。
---
3. どうしてもHTMLが必要な時の「正しい作法」
「じゃあ、HTMLを描画するのは諦めるしかないの?」と思うかもしれませんが、そうではありません。「信頼できないものを、信頼できる状態に変えてから取り込む」というプロセスを挟めばいいのです。
そのための最強のツールが [DOMPurify](https://github.com/cure53/dompurify) です。
これは、入力されたHTMLから「危ないタグ」や「怪しい属性(onerrorやjavascript:など)」を徹底的に取り除き、安全な部分だけを抽出する「空港のX線検査機」のようなものです。
実装例:安全にHTMLをレンダリングする
import DOMPurify from 'dompurify';
function SafeHtmlRenderer({ rawHtml }) {
// 1. まず、DOMPurifyで「怪しいもの」を全て除去する
const cleanHtml = DOMPurify.sanitize(rawHtml);
// 2. 検査済みの安全なHTMLだけをdangerouslySetInnerHTMLに渡す
return (
);
}
このコードでは、DOMPurify.sanitize() という強力なフィルターを通しています。仮に悪意あるスクリプトが含まれていても、この関数が綺麗に洗浄してくれるため、dangerouslySetInnerHTML を使っても安全を保つことができるのです。
---
4. セキュリティは「魔法」ではなく「習慣」
セキュリティの世界には「銀の弾丸(これさえ使えば絶対安全という万能薬)」は存在しません。
- デフォルトを信じる: Reactの自動エスケープは極めて強力です。安易に
dangerouslySetInnerHTMLを使わない。 - どうしても必要な時は検査する: 外部からのデータは全て「汚染されている」と疑い、必ず信頼できるライブラリ(DOMPurify)で洗浄する。
- 防犯意識を常に持つ: 開発中のちょっとした「便利だからこれでいいや」という妥協が、大きなインシデントの入り口になります。
今回紹介した仕組みは、Web開発における最も基本的な防犯対策です。まずは自分の書いているコードの中で、dangerouslySetInnerHTML が使われていないか、もし使われているなら「そこに適切な検査(サニタイズ)が入っているか」を一度確認してみてください。
セキュリティは、難しい技術を詰め込むことよりも、「自分の守るべきものを正しく理解し、丁寧に扱う」という姿勢から始まります。一歩ずつ、安全な開発の習慣を身につけていきましょう!
コメント