FORSMILE
EN
セキュリティ2026/09/04

【緊急】MapLibre GL JS CVE-2026-85061:6.4.1以降へ更新 — 地図を開くだけで発火するXSS

地図ライブラリ MapLibre GL JS の 6.4.0 以前に、CVSS 10.0 の XSS があります。サニタイズ処理が危険な属性を1つ飛ばして通してしまい、地図の帰属表示を描画した時点でスクリプトが動きます。npm の maplibre-gl を 6.4.1 以降へ更新してください。

ブログ一覧へ / Back to Blog

地図ライブラリ MapLibre GL JS に、CVSS 10.0(CRITICAL) のクロスサイトスクリプティング CVE-2026-85061 があります。npm パッケージ `maplibre-gl` の 6.4.0 以前が対象で、6.4.1 で修正されています。

危険なのは、これがサニタイズ処理そのもののバグだという点です。無害化しているつもりの関数が危険な属性を1つ取りこぼし、地図が帰属表示(attribution)を描画した時点でスクリプトが動きます。クリックなどの追加操作は要りません。

今すぐ行う対策

  • npm の `maplibre-gl` を 6.4.1 以降へ更新する。 2026年9月4日時点の最新版は 6.7.0(2026年9月2日公開)
  • `npm ls maplibre-gl` で、実際に入っている版と、どの依存から入っているかを確認する。 地図を使う社内ツールや管理画面が間接依存で抱えていることがある
  • すぐに更新できない場合は、開発元が示している回避策を使う。 source の `attribution` フィールドを、MapLibre へ渡す前にサニタイズする
  • 第三者が配布しているスタイル(style JSON)を読み込んでいないか確認する。 攻撃の入口は、信頼できないスタイル由来の帰属表示文字列か、ユーザーが入力できるカスタム帰属表示
  • CDN から直接読み込んでいる場合は、そのURLのバージョン番号も確認する。 package.json だけ直しても、HTML側が古い版を読んでいれば直らない
⚠ CVE Score — 最高危険度 / CRITICAL
10CRITICALCVSS v3.1CVE-2026-85061

このスコアの出所を明示します。 上の 10.0 は、GitHub(このCVEの採番機関)が付けた CVSS v3.1 の値です。ベクタは `CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:N`。

NVD にはまだ NIST 自身(Primary)の評価が載っていません。この記事を書いた時点で NVD 上の状態は「Received」で、掲載されている指標は GitHub の値だけです。後日 NIST が別の数字を付ける可能性があります。NVD を開いて違う数字が出ても、版と評価者が違うためだと考えてください。

サニタイズが危険な属性を「1つ飛ばし」で通す

MapLibre は、帰属表示に入る HTML を `DOM.sanitize()` で無害化してから描画します。その内部で、危険な属性を取り除く処理が次のように書かれていました。

`elem.attributes` をそのまま反復しながら、同じループの中で `elem.removeAttribute()` を呼ぶ、という形です。

`elem.attributes` は NamedNodeMap という「生きたコレクション」で、要素の実際の状態をそのまま映します。属性を1つ削ると、後ろの属性のインデックスが1つ前へ詰まります。ところが反復子は次のインデックスへ進むので、詰まってきた属性を1つ読み飛ばします。

html
<!-- 開発元のアドバイザリが挙げている例 -->
<details open onload="1" ontoggle="...">

<!-- onload が削除される → ontoggle のインデックスが1つ前へ詰まる
     → 反復子は次のインデックスへ進むので ontoggle を読み飛ばす
     → ontoggle が生き残ったまま innerHTML へ挿入される -->

つまり、危険な属性を2つ隣り合わせに書くだけで、2つ目が素通りします。生き残った属性は、帰属表示のコントロールが内容を `innerHTML` に入れた時点で実行されます。

開発元はこれを ゼロクリックXSS と表現しています。被害者側に必要なのは「その地図を表示すること」だけで、リンクを踏む・ボタンを押すといった追加の操作は要りません。

修正は1行、しかし他人事ではない

diff
// src/util/dom.ts の removeAttributes()

- for (const {name, value} of elem.attributes) {
+ for (const {name, value} of Array.from(elem.attributes)) {
      if (!DOM.isPossiblyDangerous(name, value)) continue;
      elem.removeAttribute(name);
  }

`Array.from()` で静的なスナップショットを取ってから反復するようにした、それだけです。修正コミットは `1da69f3`、変更は本体1行とテスト13行でした。

この形は MapLibre 固有の間違いではありません。「生きたコレクションを反復しながら、その要素を削除する」は JavaScript でよくある落とし穴です。`document.getElementsByClassName()` や `element.children`、`element.attributes` はいずれも生きたコレクションで、`querySelectorAll()` が返す静的な NodeList とは挙動が違います。

自作のサニタイザや DOM のクリーンアップ処理に同じ形が無いか、この機会に見てください。削除しながら回すループは、`Array.from()` か逆順ループにするのが定石です。

自分が影響を受けるか確認する

bash
# 実際に入っている版と、どの依存から入っているかを見る
npm ls maplibre-gl

# yarn / pnpm の場合
yarn why maplibre-gl
pnpm why maplibre-gl
  • 影響を受ける版:npm パッケージ `maplibre-gl` の 6.4.0 以前(開発元アドバイザリの表記は `<= 6.4.0`)
  • 修正版6.4.1(2026年8月18日公開)
  • 2026年9月4日時点の最新版6.7.0(2026年9月2日公開)
  • 弱点の種類:CWE-79(クロスサイトスクリプティング)
  • 開発元アドバイザリ:GHSA-jrc7-96c5-q579(2026年8月19日公開、2026年9月3日更新)

日付の関係を整理しておきます。修正版 6.4.1 が出たのは 2026年8月18日、開発元のアドバイザリは 8月19日の公開です。一方、CVE-2026-85061 として NVD に載ったのは 2026年9月3日でした。

つまり修正自体は3週間近く前から出ています。ただし、対応が終わっているかどうかを決めるのは入っている版だけです。6.4.1 以降なら終わっています。「8月に更新した」は答えになりません。対象である 6.4.0 自体が 8月16日公開だからです。

逆に、CVEの通知だけを見て動く運用をしていると、この3週間ぶん気付くのが遅れたことになります。

なお、1.x 系については本記事で開発元のソースを直接確認しました。アドバイザリの影響範囲表記は `<= 6.4.0` で、数字の上では 1.15.3 も範囲に入ります。しかし v1.15.3 のソース(`src/util/dom.js`)には `sanitize` という関数自体が存在しません。今回の欠陥がある `DOM.sanitize()` は 6.x 系のコードです。これは公式の見解ではなく、本記事が該当タグのソースを開いて確認した事実として書いています。

この件から持ち帰れること

「サニタイズしているから安全」は、サニタイザ自身が正しいことを前提にした話です。今回は無害化する側が1つ取りこぼしました。入力を信用しない設計と同じくらい、無害化処理そのものにテストがあるかが効きます。実際、修正コミットにはこのケースを踏むテストが13行追加されています。

もうひとつ。フロントエンドのライブラリは「自分が入れた覚えがない」経路で入ってきます。地図を出すのは自社サイトの一部だけでも、管理画面や社内ツールが同じパッケージを間接依存で抱えていることがあります。`npm ls` を打つのは1分です。

🚨 「しまった!」と思ったら、今すぐこちらへ

被害に遭った直後・遭ったかもしれない時のための、状況別の緊急対応ガイドです。落ち着いて、上から順に対処すれば大丈夫です。

Related articles