Pillow 脆弱性 CVE-2026-112900(GHSA-w889-685w-c8fh)は、EPS 画像を描画するときに入力ファイルの名前がそのまま Ghostscript のコマンドライン引数になる問題です。勧告によれば影響は 2.6.0〜12.3.0 で、修正は 2026-10-16 公開の 13.0.0 だけです。本記事は勧告と修正コミットを読み、12.3.0 と 13.0.0 で Ghostscript に渡る引数を偽の gs で記録して違いを確かめました。

TIMELINE時系列

修正は13.0.0。12.x への修正版は出ていない

  1. 10/16

    Pillow 13.0.0 を公開

    ファイル名を絶対パスに直す修正(#10090)を含む。Python 3.11 以上が必要

  2. 10/17

    Pillow が勧告を公開

    GHSA-w889-685w-c8fh・影響は 2.6.0〜12.3.0

  3. 10/21

    GitHub の勧告 DB に収録

    Critical(CVSS v4 9.2)。Pillow 側の表記は Moderate

出典: GitHub Advisory Database・Pillow の勧告とリリースノート・Git のタグ(2026-10-22時点)

30秒でわかる Pillow 脆弱性 CVE-2026-112900(2026-10-22時点)

  • ・何が起きたか:EPS の描画で、ファイル名が無害化されずに Ghostscript の入力ファイル引数へ入る。- や @ で始まる名前は、オプションや引数ファイルの指定として解釈されうる
  • ・影響と修正:2.6.0〜12.3.0 が影響、13.0.0 で修正。12.x への修正版は無い
  • ・深刻度:GitHub の勧告 DB は Critical(CVSS v4 9.2)、Pillow 側は Moderate
  • ・条件:Ghostscript が呼べる、EPS が描画まで進む、外から来た名前のままパスの先頭に置いて開く、の3つがそろう経路

まずやること

  • ・自分で直す範囲:アプリの依存を Pillow 13.0.0 以上に上げる。Python 3.10 以下で上げられないなら、Image.open の formats で EPS を外す
  • ・配布元に任せる範囲:OS パッケージ(apt の python3-pil など)やベンダー製イメージに同梱された Pillow は、各配布元の告知で修正状況を確認する

依存ライブラリ経由の攻撃全般はサプライチェーン攻撃とは|手口・防御ツール比較・npm 12の新機構まで実践解説にまとめています。

Pillow 脆弱性 CVE-2026-112900 の時系列と深刻度

修正は 10 月 1 日に main へ入り(PR #10090)、10 月 16 日の 13.0.0 に含まれて公開されました。Pillow の勧告の公表日は 10 月 17 日、GitHub Advisory Database への収録は 10 月 21 日です。Git のタグを見ると 12.x 系の最新は 12.3.0(2026-07-01)のままで、修正は入っていません。

深刻度は出典で分かれています。

出典 深刻度 影響する版 修正版
GitHub Advisory Database Critical(CVSS v4 9.2、攻撃の前提条件あり) 2.6.0 以上 13.0.0 未満 13.0.0
Pillow リポジトリの勧告 Moderate(スコアの記載なし) 2.6.0〜12.3.0 13.0.0
Pillow 13.0.0 リリースノート Medium の欄に掲載 — 13.0.0

CWE は引数注入(CWE-88)と OS コマンド注入(CWE-78)です。EPSS は 0.431%(GitHub の表示、2026-10-22 確認)でした。勧告は「RCE につながりうる」と書いていますが、報告者の説明では、任意コマンドの実行まで進むには攻撃者が参照先のテキストファイルも用意できる必要があります。ファイル名を外部が決められない環境では、深刻度の数字ほど重くない可能性があります。ただし、その判断は自分のコードの経路を見てから行ってください。

何が起きたか:EPS の描画で、ファイル名が Ghostscript の引数になる

Pillow は EPS を自前でラスタライズせず、外部の Ghostscript(gs)を呼んで描画します。12.3.0 の EpsImagePlugin.Ghostscript() は、開いたファイルがディスク上にあるとき、その名前(fp.name)をそのまま -f の次の引数に置いていました。

HOW IT HAPPENSファイル名の通り道

ファイル名がそのまま外部コマンドの引数になる

  1. 保存

    名前を付けてディスクへ

  2. Image.open

    中身で EPS と判定

  3. load / thumbnail

    ここで描画が要る

  4. Ghostscript

    12.3.0 以前は名前そのまま

出典: Pillow 12.3.0 / 13.0.0 の EpsImagePlugin.py と修正コミット a2c3631

実測で分かった、経路の性質は次の4つです(Pillow 12.3.0、偽の gs で引数を記録)。

・形式は拡張子でなく中身で決まる。拡張子が .png でも中身が EPS なら EPS として開かれた
・Image.open だけでは Ghostscript は呼ばれない。load() や thumbnail() など、画素が要る処理で初めて呼ばれた
・引数の先頭は、渡したパス文字列の先頭と同じ。ディレクトリを前に付けた相対パスや絶対パスなら、先頭はディレクトリ名や / になった
・BytesIO で渡すと、Pillow が一時ファイルを作って絶対パスで渡した

13.0.0 の修正は1行で、fp.name を os.path.abspath() で絶対パスに直してから渡します。修正コミットのコメントは、- や @ で始まる名前がオプションや引数ファイルとして解釈されないようにするため、と説明しています。勧告によれば Ghostscript の -dSAFER はこの引数注入を止めません。報告者も、原因は EPS の中身ではなく Pillow がコマンドを組み立てる部分にあると書いています。

対策:Pillow 13.0.0 へのアップデートと、上げられないときの緩和策

基本は 13.0.0 以上へのアップデートです。13.0.0 は Python 3.11 以上が必要で(PyPI の Requires-Python)、3.10 のサポートは終わりました。メジャー版のため、Image.fromarray() の mode 引数の扱いなど、削除・変更された API もあります。テストを通してから本番に出してください。

python3 -m pip install --upgrade "pillow>=13.0.0"
python3 -c "import PIL; print(PIL.__version__)"

すぐ上げられない場合の緩和策と、それぞれの限界は次のとおりです。

緩和策 今回の経路への効き方 限界
Image.open(path, formats=[...]) で EPS を外す EPS として開かれず、UnidentifiedImageError になる(実測) 呼び出し箇所すべてに入れる必要がある。ライブラリ内部の Image.open には届かない
保存名をサーバー側で決め、絶対パスで開く 引数の先頭が / になり、外部由来の名前が先頭に来ない 名前の扱いだけを直す。EPS の描画自体は続く
不要なら Ghostscript をイメージから外す Pillow が gs を見つけられず、描画の前に止まる ImageMagick・OCRmyPDF などが Ghostscript を必要とする環境では外せない
13.0.0 に上げる 修正そのもの リリースノートには、この問題の他にも Critical 2件を含む多数の修正が並ぶ。緩和策はそれらに効かない

アップロードを受けるコードなら、形式の制限と保存名の付け直しを両方入れるのが手堅い形です。次の例は Pillow 12.3.0 と 13.0.0 の両方で、PNG を開き、EPS を拒むことを確かめました。

import os, uuid
from PIL import Image

ALLOWED = ["JPEG", "PNG", "WEBP"]  # 受け付ける形式だけを並べる

def open_upload(data_dir, data):
    path = os.path.join(os.path.abspath(data_dir), uuid.uuid4().hex)  # 保存名はサーバー側で決める
    with open(path, "wb") as f:
        f.write(data)
    return Image.open(path, formats=ALLOWED)

自システムの確認コマンド:Pillow の版・Ghostscript・EPS を開く経路

次のコマンドは、動いている Python から見た Pillow の版、Pillow が Ghostscript を見つけられるか、コードのどこで Image.open を呼んでいるかを確認します。これは判定ではなく構成の確認で、この結果だけでは安全とは言えません。

# 1. 動いている Python から見た Pillow の版(13.0.0 以上なら修正済み)
python3 -c "import PIL; print(PIL.__version__)"
# 2. Pillow が Ghostscript を見つけられるか(True なら EPS を描画できる)
python3 -c "from PIL import EpsImagePlugin; print(EpsImagePlugin.has_ghostscript())"
# 3. コードで Image.open を呼んでいる箇所と、formats= で形式を絞っている箇所
grep -rn --include='*.py' "Image.open(" .
grep -rn --include='*.py' "formats=" .
# 4. 依存の指定で Pillow がどう書かれているか(lock は次の行の version も見る)
grep -rniE "pillow" --include='requirements*.txt' --include='pyproject.toml' --include='*.lock' .

陽性対照として、Pillow 12.3.0 と 13.0.0 の環境、偽の gs がある PATH と無い PATH、Image.open と pillow の指定を置いたディレクトリでそれぞれ期待どおりの出力が出ることを確かめました。陰性対照として、それらが無いディレクトリでは何も出ず、終了コードが 1 になります。

結果の読み方には限界があります。

・1 が 12.3.0 以下でも安全なとき:2 が False で Ghostscript が無い、またはすべての呼び出しで EPS を外している
・2 が False でも危ないとき:確認した Python と本番のコンテナが別物で、本番のイメージには Ghostscript が入っている
・3 で見えないもの:torchvision や transformers のように、ライブラリが内部で Image.open を呼ぶ経路。渡しているパスの組み立て方を自分のコード側で確かめる
・最終判断の根拠:上流の修正版(13.0.0 以上)か、配布元が修正済みと告知したパッケージかどうか

コンテナイメージの中身を洗い出すなら、SBOM入門|ソフトウェア部品表でサプライチェーン攻撃を防ぐSPDX・CycloneDX・Syftの使い方の手順で Pillow の版を一覧にできます。

影響範囲:Pillow 脆弱性の影響を受けやすい AI・画像処理アプリの構成

影響を受けるのは、下の3つがそろう経路です。

AM I AFFECTED影響を受ける条件

3つがそろう経路から先に直す

  1. 1.Pillow が 2.6.0〜12.3.0

    13.0.0 以上なら、この問題の修正は入っている

  2. 2.Pillow から Ghostscript が呼べる

    PATH に gs が無ければ描画できない。ImageMagick や OCRmyPDF と一緒に入っていることがある

  3. 3.外から来たファイル名のまま開いている

    利用者が付けた名前で保存し、相対パスで Image.open に渡している

出典: GHSA-w889-685w-c8fh の記載と、Pillow 12.3.0 での実測(2026-10-22)

Ubuntu 24.04 の apt で確かめると、ImageMagick は Ghostscript を推奨パッケージに、OCRmyPDF と LibreOffice は依存に持っていました。文書を取り込む RAG の前処理など、OCR や変換の道具と Pillow が同じイメージにあると、Ghostscript も入っている可能性が高くなります。

使い方の例 EPS が描画まで進むか Ghostscript 外部由来の名前が先頭に来るか 優先度の目安
アップロード画像のサムネイル生成(名前をそのまま保存) 形式を絞っていなければ進む イメージしだい 作業ディレクトリ直下に保存して開くなら来る 高
文書取り込み・OCR の前処理(ImageMagick・OCRmyPDF と同居) 画像を描画するなら進む 入っていることが多い 受け取った名前を使うかしだい 中〜高
学習データの前処理(ImageFolder などでディレクトリから読む) 形式を絞らなければ進む イメージしだい ディレクトリを前に付けるなら来ない 中
自社が用意した固定ファイルだけを処理 自分で決めた形式だけ 関係が薄い 来ない 低

優先度は本記事の目安で、勧告の評価ではありません。依存の脆弱性を到達性で仕分ける考え方はnpm audit の結果はどれから直す?依存関係の脆弱性を到達性・EPSS・KEVで仕分ける実践手順が詳しく、Python の依存にもそのまま使えます。言語ランタイム側で「上げられないときの緩和策」を整理した例としては、PHP 脆弱性 CVE-2026-109618・109619|php2shell の修正版と更新できないときの緩和策もあります。

この記事の結論

  • ・Pillow 13.0.0 以上に上げる。12.x への修正版は無い
  • ・上げられない間は、formats で EPS を外し、保存名をサーバー側で決めて絶対パスで開く
  • ・Ghostscript の有無は本番のイメージで確かめる。OCR や変換の道具と一緒に入っていることがある

参照ソース

・GitHub Advisory Database: GHSA-w889-685w-c8fh — 影響する版・修正版・CVSS・EPSS(2026-10-22確認)
・python-pillow/Pillow のセキュリティ勧告 GHSA-w889-685w-c8fh — Pillow 側の深刻度と条件(2026-10-22確認)
・修正コミット a2c3631(PR #10090) — ファイル名を絶対パスに直す修正
・Pillow 13.0.0 リリースノート(13.0.0 タグの原文) — Security の一覧と Python 3.10 のサポート終了
・Pillow 13.0.0 の GitHub Releases
・Pillow 13.0.0(PyPI) — Requires-Python と公開日(2026-10-22確認)
・NVD: CVE-2026-112900