Modern Frontend CVEs

対象CVE: GHSA-j95f-988m-3j2f

TiptapのReDoS脆弱性を解説:Markdownパース処理に潜むサービス停止のリスク (GHSA-j95f-988m-3j2f)

TiptapのコアライブラリにMarkdown属性パース時の正規表現DDoS(ReDoS)脆弱性が発見されました。これにより、特定の悪意のあるMarkdown入力によってアプリケーションの処理が大幅に遅延し、クライアントのフリーズやサーバーのサービス停止につながる可能性があります。

はじめに

Tiptapは、ProseMirrorをベースにしたリッチテキストエディタで、その柔軟性と拡張性の高さから多くのフロントエンドプロジェクトで採用されています。MarkdownをHTMLに変換したり、その逆を行ったりする機能も強力な特徴の一つです。しかし、このMarkdownのパース処理に深刻な脆弱性が見つかりました。今回は、`@tiptap/core`で報告されたReDoS(Regular Expression Denial of Service)脆弱性「GHSA-j95f-988m-3j2f」について、技術的な詳細と影響、そして対策を日本のフロントエンドエンジニアの皆様に向けて解説します。

脆弱性の概要:ReDoSとは何か?

今回発見された脆弱性は、正規表現DDoS (ReDoS) と呼ばれるものです。ReDoSは、非効率な正規表現のパターンと特定の入力文字列の組み合わせによって、正規表現エンジンの処理時間が異常に長くなることで発生します。最悪の場合、入力文字列の長さに比例して処理時間が二次関数的(O(n^2))あるいは指数関数的に増加し、アプリケーションが長時間ブロックされたり、クラッシュしたりする可能性があります。

`@tiptap/core`では、Markdownのブロック属性(Pandocスタイル)およびインラインショートコード属性のパース処理において、不適切な正規表現が使用されていました。

技術的な詳細:二つのReDoSパス

TiptapのMarkdown属性パーサーには、主に二つのReDoSにつながるパスが存在します。

### 1. ブロック属性におけるReDoS

`packages/core/src/utilities/markdown/attributeUtils.ts` 内で使用されている正規表現 `/([a-zA-Z][\w-]*)\s*=\s*(__QUOTED_\d+__)/g` に問題があります。この正規表現は、`キー=値` の形式をパースするために使用されます。攻撃シナリオとしては、`'__QUOTED_0'.repeat(n) + '__QUOTED_0__'` のような文字列が挙げられます。この文字列には引用符が含まれていないため、正規表現の前の置換処理では変更されません。

問題は、`\w-` の部分が非常に長い `__QUOTED_0` の繰り返しを貪欲に(greedy)消費してしまう点にあります。その後、必要な等号 `=` が見つからないためマッチは失敗し、非アンカー(unanchored)な正規表現エンジンは次の `__QUOTED_0` の位置から再度マッチングを試みます。これを繰り返すことで、入力文字列の長さに応じて処理時間が二次関数的に増加(O(n^2))します。

提供されているPoC (Proof of Concept) コードで測定された結果は以下の通りです。

```js import { createAtomBlockMarkdownSpec } from '@tiptap/core' const tokenizer = createAtomBlockMarkdownSpec({ nodeName: 'probe' }).markdownTokenizer const attack = '__QUOTED_0'.repeat(2048) + '__QUOTED_0__' const source = `:::probe {${attack}} :::\n` const started = performance.now() tokenizer.tokenize(source, [], {}) console.log(performance.now() - started) ```

20,508バイトの攻撃文字列に対して、完全なトークナイザーの処理に約1.40秒かかったと報告されています。これは、同程度の長さの正常な入力が0.29ミリ秒で処理されるのと比較して、極端な遅延です。

### 2. インライン属性におけるReDoS

同様に、`packages/core/src/utilities/markdown/createInlineMarkdownSpec.ts` 内で使用されている正規表現 `/(\w+)=(?:"([^"]*)"|'([^']*)')/g` にも問題があります。ここでは、等号 `=` が続かない長い単語文字(`\w+`)の連続が攻撃ベクトルとなります。正規表現は `\w+` で文字列の残りの部分を貪欲に消費しますが、その後 `=` が見つからないためマッチは失敗します。これもまた、非アンカーな正規表現エンジンが次の文字から再度マッチングを試みるため、入力文字列の長さに応じて処理時間が二次関数的に増加します。

こちらもPoCコードが提供されています。

```js import { createInlineMarkdownSpec } from '@tiptap/core' const tokenizer = createInlineMarkdownSpec({ nodeName: 'probe', selfClosing: true }).markdownTokenizer const source = `[probe ${'0'.repeat(32768)}]` const started = performance.now() tokenizer.tokenize(source, [], {}) console.log(performance.now() - started) ```

32,776バイトの攻撃文字列に対して、約2.21秒の処理時間が測定されています。これも同程度の長さの正常な入力が0.19ミリ秒で処理されるのと比較して、著しい遅延です。

脆弱性の影響範囲

このReDoS脆弱性は、攻撃者が制御可能なMarkdown入力をパースするアプリケーションに影響を与えます。具体的な影響は以下の通りです。

<ul> <li><strong>クライアントサイド</strong>: ブラウザのメインスレッドが長時間ブロックされ、UIがフリーズする可能性があります。永続化された悪意のあるドキュメントを読み込むたびに、クライアントが繰り返しフリーズする事態も考えられます。</li> <li><strong>サーバーサイド</strong>: サーバーのイベントループがブロックされ、リクエスト処理能力が枯渇したり、サービス全体が停止したりする可能性があります。</li> <li><strong>影響を受けないケース</strong>: ProseMirror JSONのみを消費し、Markdownパースパスを全く呼び出さないエディタは、この脆弱性の直接的な影響を受けません。</li> </ul>

対象バージョンと修正状況

この脆弱性は、Tiptapのバージョン3.7.0で導入され、現在の3.29.2および`main`ブランチに至るまで影響を受けます。つまり、3.7.0以降のTiptapを利用しているすべてのユーザーは影響を受ける可能性があります。

推奨される対策

Tiptapの利用者として、この脆弱性への対策は非常に重要です。現時点での推奨事項は以下の通りです。

<ol> <li><strong>Tiptapのアップデート</strong>: Tiptapの公式チームからの修正バージョンがリリースされ次第、速やかに最新バージョンにアップデートしてください。</li> <li><strong>正規表現の修正</strong>: <ul> <li>キーと値のパーサーの前に、文字列の開始または空白文字の境界を強制する正規表現(例: `^` や `\s` を追加)を導入します。これにより、正規表現エンジンが不必要なバックトラッキングを繰り返すのを防ぎます。</li> <li>現在の複数パスのプレースホルダーやショートコードの正規表現設計を、より予測可能で堅牢な単一パスのトークナイザーに置き換えることが望ましいとされています。</li> <li>攻撃者の入力が、Tiptapが内部で使用する `__QUOTED_n__` のようなプレースホルダーと衝突しないように、引用符で囲まれた値の処理方法を見直すことも重要です。</li> </ul> </li> <li><strong>入力の検証とサニタイズ</strong>: 信頼できないソースからのMarkdown入力は、パース前に厳密に検証し、悪意のあるパターンをサニタイズするメカニズムを導入することを検討してください。</li> <li><strong>パフォーマンス監視</strong>: Markdownパース処理のパフォーマンスを監視し、異常な遅延が検出された場合にアラートを発するシステムを構築することも有効です。</li> </ol>

まとめ

TiptapにおけるMarkdownパース処理のReDoS脆弱性は、フロントエンドアプリケーションの安定性と可用性に大きな影響を与える可能性があります。悪意のある入力によって、ユーザーエクスペリエンスが著しく低下したり、サービスが停止したりするリスクを避けるためにも、この脆弱性への理解と適切な対策が不可欠です。公式からの修正を待ちつつ、ご自身のアプリケーションが影響を受ける可能性がある場合は、早急に対策を講じることを強く推奨します。

← ブログ一覧に戻る