セキュリティ

DOMPurify 脆弱性:3.4.17で直った3件の影響確認とSAFE_FOR_TEMPLATES・IN_PLACEの点検手順

DOMPurify 脆弱性:3.4.17で直った3件の影響確認とSAFE_FOR_TEMPLATES・IN_PLACEの点検手順

XSS対策のサニタイザDOMPurifyは、2026年10月12日に公開した3.4.17で3件の脆弱性を修正しました。最も重いGHSA-cg2g-xh64-v4q7はCVSS v4.0で9.2のCriticalです。ただし影響を受けるのは、SAFE_FOR_TEMPLATESやIN_PLACEといった特定のオプションを使うコードに限られます。この記事では、3件の中身、自分のプロジェクトが対象かを調べるコマンド、間接依存で古い版が残る場合の上書き方法、同じ型の不具合を繰り返さないための設計の見直しを順に整理しました。内容は2026年10月27日時点の公開情報にもとづきます。

まとめ:3.4.16以下は3.4.17へ、危ないオプションの利用箇所を先に洗い出す

修正版は3.4.17です。npmのlatestもこの版になっています。3.4.16以下を使っているなら、オプションの有無にかかわらず上げてください。脆弱性スキャナは版だけで検出するため、放置すると警告が残り続けます。

急ぐべきかどうかを決める基準は、コードの中身です。SAFE_FOR_TEMPLATESとDOMを返すモードを組み合わせている箇所、IN_PLACEとaddHookを組み合わせている箇所があれば、その画面は当日中に更新します。どちらも無ければ、通常の依存更新の枠で対応できる範囲です。利用者の入力をHTMLとして表示する画面がどこにあるか把握できていない場合は、脆弱性診断・セキュリティ診断で入力から表示までの経路ごと洗い出すと漏れが減ります。

3.4.17で直った勧告3件:設定によって異なる攻撃の成立条件

Critical 9.2のGHSA-cg2g-xh64-v4q7を含む勧告3件について、攻撃条件を確認します。

3件はどれも、DOMPurifyを特定の設定で呼んだときにだけ成立します。設定ごとに見ていきます。

SAFE_FOR_TEMPLATESとDOM返却時に式が残る条件

不具合はshadow root内の式が残るもので、DOMを返すモードとの組み合わせで起きます。

GHSA-cg2g-xh64-v4q7は、SAFE_FOR_TEMPLATESをtrueにし、さらにRETURN_DOM・RETURN_DOM_FRAGMENT・IN_PLACEのいずれかで結果をDOMとして受け取る使い方が対象です。SAFE_FOR_TEMPLATESは、{{...}}・${...}・<%...%>の3種類のテンプレート式を取り除くオプションです。

式を取り除く後処理は<template>の中身には入っていくのに、要素に付けられたshadow rootの中には入っていきませんでした。勧告の再現例では、shadow rootの中に「$」、許可されない<object>要素、「{alert(document.domain)}」を並べます。DOMPurifyが<object>を消すと前後の文字がつながり、${alert(document.domain)}という式が完成したまま残る仕組みです。後ろで動くテンプレートエンジンがこの式を評価すれば、ブラウザではXSS、サーバーではテンプレートインジェクションになります。

評価ベクトルは「AV:N/AC:H/AT:P/PR:N/UI:N」で、攻撃の条件はそろいにくい(AC:H)ものの、条件がそろえば認証も操作も要りません。数値の読み方はnginxのCVSS v4.0 9.2の読み方で同じ形式を扱っています。影響範囲は3.4.4以上3.4.16以下です。

IN_PLACEのフックで親や兄弟を消すと残るonerror:High 7.6の勧告

GHSA-mrv2-3pqr-j5jrは、IN_PLACEモードでsanitize()を呼び、addHookで登録したフックが処理中の要素の親・祖先・未処理の兄弟を消す場合に起きます。消された部分木はサニタイズを通らないまま残り、そこにある<img onerror>のようなイベントハンドラが、sanitize()が返った後で動きます。返ってきたDOMはきれいに見えるので、出力を目で確かめても気づけません。

対象のフックはbeforeSanitizeElements・uponSanitizeElement・afterSanitizeElements・beforeSanitizeAttributes・afterSanitizeAttributesの5種類です。評価はCVSS v4.0で7.6、影響範囲は3.4.16以下となっています。勧告は、3.4.16で入ったGHSA-p98j-92pf-mc4pの修正が「フックが処理中の要素そのものを消した場合」しか扱っていなかったと説明しています。

XHTMLモードのSMIL属性ガードと、CVE未採番・KEV未掲載の現状

3件目のGHSA-c6gr-qvv4-5v3fは重大度Lowです。PARSER_MEDIA_TYPEをapplication/xhtml+xmlにしたとき、SVGアニメーション(SMIL)のattributeNameにhrefを指定する手口への防御が働いていませんでした。既定のtext/htmlで使っていれば対象外です。

2026年10月27日にGitHubのAPIで確かめた時点で、3件ともCVE番号は付いていません。同日取得のCISAのKEVカタログ(2026年10月26日版)にもDOMPurifyは1件も無く、勧告に悪用の記載もありません。とはいえ再現手順は勧告に載っています。3.4.17のリリースは10月12日、GitHub Advisory Databaseへの掲載は10月22日で、手口は誰でも読める状態です。

影響確認:npm lsとgrepでdompurifyの版と危ないオプションを特定する手順

判定に要るのは、入っている版と、コードがどのオプションで呼んでいるかの2つです。

npm lsで直接依存と描画ライブラリ経由の間接依存を洗い出す

間接依存では、monaco-editorやmermaidを経由するものも確認対象です。

DOMPurifyは自分でインストールしていなくても、エディタや図の描画ライブラリから入ってきます。2026年10月27日にnpm registryで各パッケージの最新版を確かめたところ、依存の書き方は次のとおりでした。

パッケージ(最新版) dompurifyの指定 新規インストールで入る版
monaco-editor 0.57.0 3.4.15(固定) 3.4.15のまま
mermaid 12.2.0 ^3.4.12 3.4.17
jspdf 4.2.1 ^3.3.1(任意依存) 3.4.17
isomorphic-dompurify4.6.0 ^3.4.17 3.4.17

範囲指定(^)のものも、ロックファイルに古い版が記録されていれば、そのまま古い版が入り続けます。実際に解決された版はnpm lsで確かめます。

# 間接依存も含め、入っている dompurify をすべて版つきで表示する
npm ls dompurify --all

# pnpm / yarn の場合
pnpm why dompurify
yarn why dompurify

3.4.16以下が1行でも出れば更新の対象です。リポジトリ全体を定期的に点検する仕組みはOSV-Scannerでの依存の点検で扱っています。

grepで危ないオプションとフックを探し、同じ呼び出しの条件を確認

検索対象はSAFE_FOR_TEMPLATES・RETURN_DOM・IN_PLACE・addHookなどです。

急ぐべき箇所かどうかは、呼び出しのオプションで分かれます。次の検索で、自分のコードの該当箇所を一覧にします。

# 自分のコード(node_modules を除く)から、危ない組み合わせの材料を探す
git grep -nE "SAFE_FOR_TEMPLATES|RETURN_DOM|IN_PLACE|addHook|PARSER_MEDIA_TYPE" -- "*.js" "*.mjs" "*.ts" "*.tsx" "*.vue" "*.svelte"

ヒットした行を、同じsanitize()呼び出しの中で次のどれに当たるかで分けます。

  • SAFE_FOR_TEMPLATESと、RETURN_DOM・RETURN_DOM_FRAGMENT・IN_PLACEのいずれかが一緒に指定されている:GHSA-cg2gの対象
  • IN_PLACEを使い、addHookのフック内でremove()やremoveChild()を呼んでいる:GHSA-mrv2の対象
  • PARSER_MEDIA_TYPEにapplication/xhtml+xmlを指定している:GHSA-c6grの対象

どれにも当たらなければ、今回の3件が実害になる経路はありません。ただしmonaco-editorのような間接依存がどう呼んでいるかは、この検索では分かりません。版の更新はどのみち済ませます。

3.4.4〜3.4.16かどうかで変わる3件の影響範囲と条件の早見表

3件は影響範囲の下限が違います。2系を使っている場合の扱いもここで確認します。

勧告 重大度 影響範囲 成立する設定
GHSA-cg2g-xh64-v4q7 Critical 9.2 3.4.4〜3.4.16 SAFE_FOR_TEMPLATES+DOM返却
GHSA-mrv2-3pqr-j5jr High 7.6 3.4.16以下 IN_PLACEと親・兄弟を消すフック
GHSA-c6gr-qvv4-5v3f Low 3.4.16以下 XHTMLとして解析する設定

3.4.3以下ならGHSA-cg2gは対象外ですが、残り2件は範囲に入ります。2系の最終版は2026年3月5日公開の2.5.9で、今回の修正は2系には出ていません。READMEによれば2.5.9はInternet Explorerを支えた最後の版で、IE対応が要らないなら3系へ移る以外に修正を受け取る道はありません。

更新手順:3.4.17への上げ方とmonaco-editorの固定版を上書きする方法

直接依存なら版を上げるだけで済みます。手間がかかるのは、他のパッケージが古い版を固定している場合です。

package.jsonとロックファイルで3.4.17を下限にする書き方

直接依存しているなら、指定を3.4.17以上にしてロックファイルを更新します。

npm install dompurify@^3.4.17
npm ls dompurify --all   # 3.4.17 以外が残っていないか確かめる

更新を自動のプルリクエストで受け取る設定は、dependabot.ymlの書き方にまとめています。DOMPurifyは2026年だけで3系を3.3.2から3.4.17まで20回リリースを重ねており、手作業で追い続けるのは現実的ではありません。

monaco-editorの固定版をoverridesで上書きする方法

対象は、monaco-editor 0.57.0がdompurifyを3.4.15に固定している場合です。

monaco-editorの最新版0.57.0は、dompurifyを範囲ではなく3.4.15ちょうどで指定しています。monaco-editorを上げても、この版が3.4.17に変わることはありません。npmならpackage.jsonのoverrides、pnpmならpnpm.overrides、yarnならresolutionsで上書きします。

{
  "dependencies": {
    "dompurify": "^3.4.17",
    "monaco-editor": "0.57.0"
  },
  "overrides": {
    "dompurify": "$dompurify"
  },
  "pnpm": {
    "overrides": { "dompurify": "^3.4.17" }
  },
  "resolutions": {
    "dompurify": "^3.4.17"
  }
}

3つの書き方は、使っているパッケージマネージャの分だけ残します。npmでは、直接依存にも書いているパッケージへ別の版をoverridesで指定するとEOVERRIDEエラーになります。npmの説明どおり「$dompurify」と書き、dependenciesの指定を参照させるのが安全です。3.4.15から3.4.17はパッチ版の差なので、エディタの表示に関わる回帰が無いかを画面で確かめたうえで反映します。

サーバー側のjsdom併用とDOMPurifyの依存をそろえる更新方法

ここではjsdomとの組み合わせと、isomorphic-dompurify 4.6.0への更新を扱います。

Node.jsでDOMPurifyを使うには、jsdomでwindowを作って渡します。GHSA-cg2gの勧告は、サーバー側でテンプレートエンジンが式を評価する場合にテンプレートインジェクションに至りうると書いており、サーバー側の利用も対象から外れません。

DOMPurifyのREADMEは、jsdomもサニタイズの安全性を支える部品だとして、古いjsdomには既知のバイパスがあるため最新版を使うよう求めています。happy-domとの組み合わせは安全とみなしていません。両方をまとめて扱うisomorphic-dompurifyなら、最新の4.6.0がdompurify ^3.4.17とjsdom ^30.1.3を指定しているので、4.6.0へ上げれば両方がそろいます。

繰り返す同型の不具合:特定のオプションに頼る設計をやめる判断

見直す対象は、SAFE_FOR_TEMPLATESとIN_PLACEに頼る設計です。

今回の3件は単発ではありません。同じオプションの周りで、修正と回避が繰り返されてきました。ここからは、版を上げるだけで終わらせない判断の話です。

SAFE_FOR_TEMPLATESの回避の連鎖と本番利用を避ける判断

回避は2026年に3回あり、READMEも本番利用を勧めていません。

SAFE_FOR_TEMPLATESの回避は、2026年4月のGHSA-crv5-9vww-q3g8(RETURN_DOMモード、3.4.0で修正)、6月のGHSA-gvmj-g25r-r7wr(<template>の中身、3.4.8で修正)、今回のshadow rootと3回続きました。式が隠れられる場所を1つずつ塞いでいる状態です。

READMEはこのオプションを本番では勧めておらず、利用者が書いたHTMLをテンプレートとして解析させるべきではないと書いています。結論は1つです。利用者の入力をDOMPurifyに通したあとでVueやAngularのテンプレート、サーバーのテンプレートエンジンに渡して評価させる設計は、DOMPurifyの版にかかわらずやめます。サニタイズ後のHTMLは表示するだけにし、テンプレートとして解釈させないことです。

IN_PLACEのフック由来の勧告から見る不完全修正が続いた経緯

修正は3.4.13・3.4.16・3.4.17と続き、フック由来の勧告が連鎖しています。

IN_PLACEとフックの組み合わせも同じ形をしています。8月のGHSA-55q2-fjhq-7xh7が3.4.13、9月のGHSA-p98j-92pf-mc4pが3.4.16、今回のGHSA-mrv2が3.4.17で直りました。いずれも「フックで要素を消すと、切り離された部分木のイベントハンドラが残る」という同じ現象の別経路です。

IN_PLACEは渡したDOMをその場で書き換えるので、文字列の受け渡しより速く動く場面があります。しかしREADMEも、攻撃者の制御するツリーはライブのDocumentに接続する前にサニタイズするよう注意しています。体感できるほどの速度差が測定で確認できていないなら、IN_PLACEは使いません。フックでどうしても要素を消したいときも、消すのは処理中の要素自身に限ります。

文字列で受けて文字列で返す既定の使い方へ戻すコードとCSPの併用

3件とも、既定の使い方、つまり文字列を渡して文字列を受け取る呼び方では成立しません。設定を足す前に、まずこの形で足りないかを確かめます。

import DOMPurify from 'dompurify';

// 文字列で受けて文字列で返す。RETURN_DOM・IN_PLACE・SAFE_FOR_TEMPLATES は使わない
export function renderUserHtml(el, dirty) {
  if (!DOMPurify.isSupported) {
    // サニタイズできない環境では HTML として表示しない
    el.textContent = dirty;
    return;
  }
  el.innerHTML = DOMPurify.sanitize(dirty, { USE_PROFILES: { html: true } });
}

isSupportedは、DOMPurifyが処理できる環境かを示すプロパティとしてREADMEに載っています。サポート外の古いブラウザでは入力をそのまま返すため、その場合の表示方法は、HTMLではなくテキストです。MarkdownからHTMLを作る画面なら、react-markdownとrehype-sanitizeで描画する方法のように、HTMLを組み立てる段階で許可リストを当てる選択肢もあります。

サニタイザは1層の防御にすぎません。今回のような回避が見つかっても被害を止められるよう、CSPのnonce方式の設定でインラインスクリプトの実行そのものを止めておきます。DOMPurifyの脅威モデルの説明も、守る範囲と守らない範囲を分けて書いています。XSSそのものの仕組みはXSSとCSRFの違いと実装レベルの対策で整理しました。

DOMPurifyの脆弱性と3.4.17への更新でよくある質問と回答

3.4.17への更新で出てきやすい疑問に、公開されている一次情報の範囲で答えます。

SAFE_FOR_TEMPLATESもIN_PLACEも使っていなければ急がなくてよいですか?

当日中の対応は要りませんが、更新は済ませてください。CriticalとHighの2件は、それぞれのオプションを使っていなければ成立しません。Lowの1件もXHTMLとして解析する設定が前提です。ただし、間接依存のライブラリが内部でどのオプションを使っているかは自分のコードの検索では見えません。脆弱性スキャナも版だけで検出し続けるので、次の依存更新の機会に3.4.17へ上げるのが妥当です。

2系(2.5.9)を使っている場合はどうすればよいですか?

Criticalの件は影響範囲が3.4.4以上なので対象外です。一方、HighとLowの2件はGitHub Advisory Databaseの表記で「3.4.16以下」とされ、2系も範囲に含まれます。2系の最終版は2026年3月5日の2.5.9で、今回の修正版は出ていません。Internet Explorer対応が不要なら3系の3.4.17へ移ってください。IE対応が必要な画面では、IN_PLACEとXHTML解析を使わない形で運用するしかありません。

CVE番号が付いていないのは、対応しなくてよいという意味ですか?

違います。CVE番号は採番の手続きの問題で、危険度とは関係しません。2026年10月27日時点で3件ともCVEは付いていませんが、GitHub Advisory Databaseには掲載済みで、npm auditやDependabotはGHSA番号で検出します。CVE番号とGHSA番号の関係はCVEの番号の読み方とNVDとの違いで説明しています。

Node.jsのサーバー側で使っている場合も影響を受けますか?

サーバー側での利用も影響の対象です。GHSA-cg2gの勧告は、サーバー側でテンプレートエンジンが残った式を評価するとテンプレートインジェクションになりうると書いています。サーバー側ではDOMPurifyの版に加えて、DOMを作っているjsdomの版も確認します。READMEはjsdomを最新に保つよう求めており、happy-domとの組み合わせは安全とみなしていません。

XSSで個人情報が漏れたら、どこかへ報告する義務はありますか?

不正アクセスによる個人データの漏えいやそのおそれがあれば、個人情報保護法にもとづき個人情報保護委員会への報告と本人への通知が必要になる場合もあるため、報告対象かどうかの確認が必要です。XSSで会員の情報が外部へ送られた疑いがあるなら、まず行うべき対応はアクセスログの保全です。報告の期限と対象の4類型は個人情報保護委員会への報告義務と速報・確報の実務で整理しています。自社の画面にどれだけ入力の表示箇所があるかを外部の目で確かめるなら、脆弱性診断の進め方と外注時の判断基準が入口になります。

関連記事

お気に入りに入れた記事の一覧

資料請求

今日のトレンド記事 直近 24 時間で、いつもより多く読まれている記事

  1. 2026.07.09 テックブログ Muse Imageとは?Metaの画像生成AIの使い方・API料金(1枚0.01ドル)と日本での提供状況【2026年10月】
  2. 2026.10.21 テックブログ ユミルリンク不正アクセス第二報で延べ5,162万件:Cuenote FC利用企業と受信者の確認点
  3. 2026.10.23 テックブログ Renta!はいつ再開?不正アクセスでメールアドレス等が流出したおそれ・再開時のパスワード再設定と運営側の対応
  4. 2026.10.23 テックブログ Splunk 脆弱性CVE-2026-76299:影響版の確認と10.2.8などへの更新手順
  5. 2026.10.22 テックブログ Tomcatのバージョン一覧とサポート期限【2026年10月】最新版11.0.27、9.0系は2027年3月末終了・9.1系は2030年末まで

RELATED POSTS 関連記事

目次