CairoSVG: Quadratic時間のDoS攻撃における、巧妙に作成されたSVGの`<path>`要素の解析
## 概要
信頼できないSVGファイルにおいて、`<path d="...">`属性に多数のセグメントが含まれている場合、CPU使用量はO(n²)となります。1MiB以下の単一の`<path>`要素の処理に、数十秒かかることがあります。`cairosvg/path.py`ファイル内に、独立したO(n²)の処理が2箇所存在します。
1. **Tokenizer**: パスデータのパーサーが、`d`文字列を`while string:`ループで繰り返しスライス/再スキャンします(各ステップは残りの文字列の長さに比例し、全体として属性に対してO(n²)となります)。
2. **draw_markers**: マーカーの処理において、`node.vertices`が`while node.vertices: ... node.vertices.pop(0)`によって処理されます。`list.pop(0)`はO(n)であるため、n個の頂点を処理するにはO(n²)となります。
これらの処理は、通常のレンダリングパス(`svg2png`/`svg2pdf`)において実行され、攻撃者はSVGドキュメントの内容のみを制御できます。
## 実証コード (cairosvg 2.9.0をインストール済み)
```python
import cairosvg
d = "M0 0 " + "L1 1 " * 100000
svg = f'<svg xmlns="http://www.w3.org/2000/svg" width="10" height="10"><path d="{d}"/></svg>'
cairosvg.svg2png(bytestring=svg.encode()) # 488 KBのドキュメントの場合、約4.4秒
```
| パスセグメント数 | SVGファイルサイズ | 処理時間 |
|---|---|---|
| 50,000 | 244 KB | 1.14秒 |
| 100,000 | 488 KB | 4.36秒 |
| 200,000 | 約960 KB | 約18秒 |
セグメント数を2倍にすると、処理時間は約4倍になる ⇒ 二次関数的な増加。1MiB以下の入力では、約18秒のCPU時間が必要となり、ユーザーが提供したSVGファイルをレンダリングするサービス(サムネイル、アバター、PDFエクスポートなど)は、DoS攻撃の対象となる可能性があります。
## 脆弱性の箇所
信頼できないSVG文字列に対する、公開APIである`svg2png`、`svg2pdf`、`svg2ps`。
## 修正案
残りの文字列を再スライスする代わりに、単一の順方向スキャン/インデックス(または`re.finditer`)を使用します。また、`vertices`を`list.pop(0)`ではなく、インデックスまたは`collections.deque.popleft`を使用して処理します。オプションで、パスセグメントの数を制限することもできます。
影響を受けるパッケージ:
- pip (Python) cairosvg: 影響を受けるバージョンは2.9.0以下、2.9.1で修正済み。