[緊急解説] `@xmldom/xmldom`のsetAttribute()脆弱性 (GHSA-4w3w-2rp5-g8jm) - フロントエンドへの影響と対策
はじめに:なぜフロントエンドエンジニアがXML?
XMLと聞くと、バックエンド処理や古くからの技術を想像し、「自分たちのフロントエンドには関係ない」と思われるかもしれません。しかし、現代のフロントエンド開発でも、SVGの操作、特定のデータ形式のパース、あるいはサードパーティライブラリが内部でXMLパーサー(`@xmldom/xmldom`など)を使用しているケースは少なくありません。そうした状況で、今回報告された高深刻度の脆弱性が、あなたのアプリケーションに深刻な影響を与える可能性があります。
脆弱性の概要:`@xmldom/xmldom`の危険な`setAttribute()`
今回報告されたGHSA-4w3w-2rp5jm(CVE-2026-83605)は、`@xmldom/xmldom`ライブラリの`Element.setAttribute()`メソッドにおける属性名の検証不備に起因する、非常に危険な脆弱性です。
通常、XMLの属性名(QName)には厳密な形式が求められますが、`setAttribute()`が内部的に呼び出すメソッドがこのバリデーションをスキップしていました。このため、開発者が`setAttribute()`を使用して、例えば `class="safe" onclick` のように、属性名を途中で閉じ、新しい属性を挿入するような不正な文字列を設定しても、エラーが発生せずに受け入れられてしまう問題がありました。
なぜ危険?具体的な影響
この脆弱性の最大の問題点は、不正な属性名がXMLシリアライザ(`XMLSerializer().serializeToString()`)によって**そのまま出力されてしまう**点です。属性値は適切にエスケープされますが、属性名に含まれる引用符やスペースが、現在の属性を途中で閉じ、攻撃者が任意の追加属性を注入するきっかけとなります。
これにより、以下のような深刻なセキュリティリスクが発生する可能性があります。
**クロスサイトスクリプティング (XSS)**: 最も懸念されるのはXSSです。`onclick="alert(document.domain)"`のようなイベントハンドラを注入され、ユーザーのブラウザ上で悪意のあるスクリプトが実行される恐れがあります。
**セキュリティ属性の上書き**: `integrity`、`nonce`、`sandbox`、`Content-Security-Policy`といった、Webアプリケーションのセキュリティを担保する重要なHTML/XML属性が、攻撃者の意図する値で上書きされる可能性があります。これにより、CSPをバイパスされたり、リソースの信頼性が損なわれたりするリスクがあります。
**API挙動の不整合**: `Document.createAttribute()`は属性名を正しく検証するのに対し、`setAttribute()`は検証しないという一貫性のない挙動は、開発者がセキュリティ判断を誤る原因にもなりえます。
あなたのプロジェクトは大丈夫?確認ポイント
この脆弱性の影響を受けるのは、アプリケーション内で`@xmldom/xmldom`を使用しており、特に**ユーザー入力や外部から取得したデータを元にXML要素の属性を設定し、その後シリアライズして出力している**場合です。
例えば、以下のようなケースは特に注意が必要です。
ユーザーがアップロードしたSVGファイルをNode.jsなどのサーバーサイド環境で`@xmldom/xmldom`を使って処理・加工し、フロントエンドに返している場合。
特定のXMLベースのデータ構造をクライアントサイドでパース・操作し、その結果をDOMに挿入している場合。
まずは、ご自身のプロジェクトの`package.json`や依存関係を調べ、`@xmldom/xmldom`パッケージが使用されているか、またそのバージョンがいくつかを確認してください。
今すぐできる対策:必須の`{ requireWellFormed: true }`オプション
この脆弱性への対策は、ライブラリのバージョンアップと**手動での設定変更**が不可欠です。
### ステップ1: `@xmldom/xmldom`のバージョンアップ
まず、`@xmldom/xmldom`を**0.9.11**または**0.8.14**以降のバージョンにアップデートしてください。これにより、後述するセキュリティオプションが利用可能になります。
例: `npm install @xmldom/xmldom@latest` または `yarn upgrade @xmldom/xmldom@latest`
### ステップ2: `XMLSerializer().serializeToString()`に`{ requireWellFormed: true }`を指定(必須!)
バージョンアップ後も、最も重要な対策は`XMLSerializer().serializeToString()`を呼び出す際、**必ず**オプションとして`{ requireWellFormed: true }`を渡すことです。このオプションを指定すると、シリアライズ時に各属性名が有効なXML QNameであるか厳密に検証され、無効な場合は`InvalidStateError`がスローされて出力が停止されます。
コード例:
```javascript const { DOMParser, XMLSerializer } = require('@xmldom/xmldom'); const parser = new DOMParser(); const doc = parser.parseFromString('<root />', 'application/xml'); // 悪意のある属性名をセット doc.documentElement.setAttribute('a class="evil" onclick', 'alert(document.domain)'); console.log('--- 脆弱性あり(デフォルト動作)---'); try { const vulnerableOutput = new XMLSerializer().serializeToString(doc); console.log('Vulnerable Output:', vulnerableOutput); } catch (e) { console.error('Error (vulnerable, attribute name is still serialized):', e.message); } console.log('\n--- 脆弱性対策済み({ requireWellFormed: true }を指定)---'); try { const secureOutput = new XMLSerializer().serializeToString(doc, { requireWellFormed: true }); console.log('Secure Output (should throw error or be clean if attribute was prevented):', secureOutput); } catch (e) { console.error('Error (secure, InvalidStateError should be thrown):', e.message); console.log('XML serialization was prevented due to invalid attribute name.'); } ```
**既存コードの監査**: アプリケーション内で`serializeToString()`を呼び出しているすべての箇所を洗い出し、`{ requireWellFormed: true }`オプションが正しく適用されていることを確認してください。デフォルトの動作は変更されていないため、この手動設定が必須となります。
まとめ
`@xmldom/xmldom`の`setAttribute()`に関する脆弱性は、XSSや重要なセキュリティ属性の上書きなど、フロントエンドにも深刻な影響をもたらす可能性があります。Webアプリケーションの安全性を保つためにも、この脆弱性への理解と迅速な対策が不可欠です。
フロントエンドエンジニアとして、たとえ直接XMLを扱わない場合でも、使用しているライブラリの依存関係を常に意識し、脆弱性情報にアンテナを張ることが重要です。この情報を参考に、ぜひご自身のプロジェクトのセキュリティ対策を再確認してください。安全なWebアプリケーション開発を心がけましょう。