【実務・中級編】DOMPurifyを用いたクライアントサイドでのHTMLサニタイズ実装 – アプリケーションセキュリティ & 安全な開発防御ガイド

DOMPurifyで防ぐ「盲点」:XSSは単なる「アラートが出る」問題ではない

現場のエンジニア諸君、今日もコードの最前線でお疲れ様。

セキュリティの現場に長くいると、「クロスサイトスクリプティング(XSS)? ああ、を投げればいいんでしょ?」と、高を括っている若手に時折出会う。だが、現実はそんなに甘くない。

攻撃者は、ブラウザのパーサーが「これはコードだ」と誤認する、もっと泥臭くて巧妙な「タグの隙間」を狙ってくる。特に現代のSPA(Single Page Application)やCMSのように、ユーザーが入力したHTMLを動的にレンダリングするアプリケーションは、DOMを直接操作する過程で常に「自爆」の危険と隣り合わせだ。

今日は、そんな泥沼から我々を救い出してくれる「DOMPurify」の正しい使い方について、教科書には載っていない実践的な知見を共有しよう。

—

なぜDOMPurifyなのか? 〜「ブラックリスト」は死に体である〜

まず大前提だ。正規表現や自作の置換関数でHTMLをサニタイズしようとするのは「ザルで水をすくう」のと同じだと心に刻んでほしい。

例えば、攻撃者は次のようなペイロードを平気で投げてくる。



クリックしてね

これらを個別に弾こうとすれば、ブラックリストはすぐに肥大化し、メンテナンス不能になる。DOMPurifyが優れているのは、ブラウザのDOM解析エンジンを利用して「安全な要素と属性のみをホワイトリスト方式で通過させる」という、極めて堅牢な設計思想にあるからだ。

—

【実戦】DOMPurifyのセキュアな実装パターン

DOMPurifyを導入する際は、デフォルト設定を過信してはいけない。アプリケーションの要件に合わせて、「徹底的に絞り込む」のがプロの流儀だ。

以下は、ReactやVue、あるいはプレーンなJavaScript環境で活用できる、最も推奨される実装例だ。

JavaScript実装サンプル

import DOMPurify from ‘dompurify’;

/

  • ユーザー入力を受け取り、安全なHTMLとして返す関数
  • @param {string} dirtyHtml – 信頼できない入力文字列
  • @returns {string} – サニタイズ済みの安全なHTML

/
function sanitizeInput(dirtyHtml) {
return DOMPurify.sanitize(dirtyHtml, {
// 1. 基本設定:許可リストを最小限に絞る
ALLOWED_TAGS: [‘b’, ‘i’, ‘em’, ‘strong’, ‘a’, ‘p’, ‘br’],
ALLOWED_ATTR: [‘href’, ‘title’, ‘target’],

// 2. リンクの安全性を強制する
// javascript: プロトコルなどを完全にブロックする
ALLOWED_URI_REGEXP: /^(?:(?:(?:f|ht)tps?|mailto|tel|callto|cid|xmpp):|[^a-z]|[a-z+.\-]+(?:[^a-z+.\-:]|$))/i,

// 3. リンクは必ず新しいタブで開き、リファラを遮断(セキュアなリンク設計)
ADD_ATTR: [‘target’],
FORBID_TAGS: [‘style’, ‘script’, ‘iframe’, ‘object’],

// 4. 解析結果が不審な場合にエラーを投げる(厳格モード)
RETURN_DOM: false,
RETURN_DOM_FRAGMENT: false,
});
}

// 利用イメージ
const userInput = ‘Hello‘;
const cleanHtml = sanitizeInput(userInput);
// 結果: Hello (imgタグは属性を含めて綺麗に消える)

—

運用で絶対に忘れてはいけない「3つの鉄則」

コードを書くだけがセキュリティではない。運用フェーズでこれを維持するための防衛線を張っておく必要がある。

1. CSP(Content Security Policy)との二段構え

DOMPurifyを突き抜けるような未知のゼロデイ脆弱性が見つかったとしても、CSPが設定されていれば被害を最小限に抑えられる。Nginx等で以下のヘッダーを必ず付与せよ。

Nginx設定例
add_header Content-Security-Policy “default-src ‘self’; script-src ‘self’; object-src ‘none’;”;

特に script-src 'unsafe-inline' を許可しない運用を目指すのが、現代のWebエンジニアの責務だ。

2. ライブラリのアップデートを自動化する

DOMPurifyのようなサニタイズライブラリは、ブラウザの仕様変更(バイパス手法)を追うために頻繁にアップデートされる。npm audit や Dependabot を導入し、常に最新版を追いかける体制を構築すること。

3. DOMPurifyだけで完結させない(多層防御)

もしバックエンドにPHPやPythonを使っているなら、サーバーサイドでもバリデーションを行うのが鉄則だ。クライアントサイドのサニタイズは「UX向上のための即時フィードバック」と割り切り、最終的な保存データはサーバー側で HTMLPurifier (PHP) 等を用いて再精査する。「クライアントのデータは全て悪意がある」という性悪説こそが、我々エンジニアの生存戦略だ。

—

最後に:セキュリティは「終わりのない旅」だ

セキュリティ対策は、一度コードを書いて終わりではない。攻撃者は常に「次の隙間」を探している。

今回紹介したDOMPurifyの実装は、あくまで「最低限の防御壁」に過ぎない。しかし、この壁があるかないかで、インシデント発生時の被害規模は劇的に変わる。

君たちが書くコードが、誰かの大切な情報を守る盾になる。その誇りを持って、日々の開発に取り組んでほしい。もし「この実装で本当に大丈夫か?」と不安になったら、いつでもこのブログを読み返してくれ。

現場からは以上だ。健闘を祈る。

コメント

タイトルとURLをコピーしました