社員の顔写真や、申請書に添付された画像をアップロードさせ、一覧用のサムネイルを Pillow で作る。Django や FastAPI で作ったそんな業務システムの依存を10月21日に pip-audit で調べると、pillow 12.3.0 に8件の脆弱性が出ました。修正版の欄はすべて 13.0.0 です。
12系のまま直す方法はありません。PyPI にある 12系の最後の版は7月1日の 12.3.0 で、13.0.0 は Python 3.11 以上でしかインストールできません。さらに、13.0.0 のリリースノートに載る脆弱性の修正は43件あり、監査ツールに出るのはその一部です。CRITICAL の JPEG の件は、10月21日の時点で pip-audit に出ませんでした。
本記事は、どの処理で当たるかを条件ごとに見分け、13.0.0 に上げる前に互換性の変更を確かめ、Python 3.10 の環境をどう進めるかを整理したものです。
修正は13.0.0だけ、Python 3.11以上が必要
| 版 | PyPI への公開(UTC) | 必要な Python | 10月の修正 |
|---|---|---|---|
| 12.3.0 | 2026-07-01 | 3.10 以上 | 含まない(12系の最後の版) |
| 13.0.0 | 2026-10-16 | 3.11 以上 | 含む |
10月20日(UTC)に GitHub Advisory Database に公開された Pillow のアドバイザリは9件で、修正版はすべて 13.0.0 です。重大度は CRITICAL 1件、HIGH 6件、MODERATE 2件でした。13.0.0 のリリースノートは、後方互換性のない変更の最初に「Python 3.10 のサポートを終了した(2026年10月にサポート終了を迎えたため)」と書いています。Python 側でも、3.10 のセキュリティ修正は10月1日の 3.10.22 が最後でした(PEP 619)。
編集部の検証環境で、対象の Python の版を指定して依存を解決すると、次のようになりました。
--python-version 3.10 : pillow -> pillow==12.3.0
--python-version 3.10 : pillow>=13.0.0 -> unsatisfiable(pillow==13.0.0 only supports >=3.11)
--python-version 3.11 : pillow -> pillow==13.0.0
Python 3.10 で動かしているシステムは、Pillow だけを上げることができません。上限を指定していない requirements.txt でも、3.10 の環境では 12.3.0 のまま止まり、エラーにもなりません。
監査ツールに出るのは一部
13.0.0 のリリースノートの Security の節には、10月21日の時点で39項目・GHSA の ID で43件の修正が載っています。重大度の区分は Critical 3件、High 14件、Medium 23件、Low 3件です。公開時の文書は38項目・42件で、10月19日に Medium の1件(GHSA-654x-cwwv-5j9c)と、割り当てられた CVE の ID が書き足されました。ドキュメントのサイトでは、最新の文書(latest)が43件、stable の文書は42件のままでした。
この43件を GitHub Advisory Database で1件ずつ調べると、10月21日の時点で収録されていたのは上の9件だけでした。残りの34件のうち32件は、Pillow のリポジトリのセキュリティアドバイザリとして10月17〜19日に公開されていますが、Advisory Database には入っていません。さらに、pip-audit(既定の PyPI の脆弱性情報)で 12.3.0 を調べた結果は8件で、CRITICAL の GHSA-8g5g-8vmr-9782(JPEG のデコーダーのヒープバッファーオーバーフロー)が含まれていません。PyPI の 12.3.0 の脆弱性情報にも、この1件はありませんでした。
後から書き足された GHSA-654x は、環境変数 PILLOW_ALIGNMENT を2以上にして画像を Arrow 形式で書き出すと、前に使ったメモリの内容が混ざる件です。リポジトリのアドバイザリは対象を「11.2.1 - 13.0.0」とし、修正版の欄が空のままですが、リリースノートは 13.0.0 の修正として載せています。編集部の検証環境(macOS 26.5.1、Python 3.12.12)でアドバイザリの再現コードを動かすと、12.3.0 では書き出した2440バイトのうち114バイトが、その画像の画素ではない値になり、13.0.0 では「Image rows are not contiguous in memory.」の例外で書き出しが止まりました。13.0.0 で直っていると読めます。

重大度の区分も、リリースノートと Advisory Database で一致しません。ゼロ幅の画像の件(GHSA-58r4)と GIF の件(GHSA-65qm)は、リリースノートでは Low、Advisory Database では HIGH です。監査ツールの件数や重大度で更新の優先度を決めず、12.3.0 以前を使っているなら、43件すべての修正を受けるために 13.0.0 へ上げる前提で計画します。監査ツールの表示は、情報が更新されれば変わります。
処理ごとの当たる条件
Advisory Database の9件の本文に書かれた条件です。重大度は Advisory Database のものです。
| ID(重大度) | 当たる処理 | 条件の要約 |
|---|---|---|
| GHSA-8g5g(CRITICAL) | JPEG の thumbnail() | 縮小デコード(draft())を使う経路。formats=["JPEG"] では防げない |
| GHSA-c6hw・q45j(HIGH)、462x(MODERATE) | TIFF を開いて読み込む | 既定の設定で当たる。ヒープのメモリの内容がピクセルとして返る、またはクラッシュ |
| GHSA-pchr(HIGH) | TIFF を TIFF で保存し直す | 読み込んだメタデータを保ったまま libtiff で保存する |
| GHSA-65qm(HIGH) | GIF のフレームを順に読む | seek() の例外を捕まえて同じ画像を使い続ける |
| GHSA-p4wg(HIGH) | PDF への追記 | 利用者の PDF に save(..., append=True) で追記する |
| GHSA-58r4(HIGH) | ImageMorph の処理 | 幅0の PPM・PGM を受け付けて形態処理をかける |
| GHSA-vx6w(MODERATE) | EPS の読み込み | Ghostscript を入れ、load(scale=N)(N>1)で描画する |
JPEGのサムネイル:thumbnail()の縮小デコード
GHSA-8g5g は、thumbnail() が自動で呼ぶ JpegImageFile.draft() が JPEG を1/2・1/4・1/8 に縮小してデコードしようとし、可逆圧縮(SOF3)の JPEG では縮小されないまま書き込まれて、ヒープのバッファーを越えるという問題です。アドバイザリによれば、ファイルは規格どおりの JPEG なので、Image.open(..., formats=["JPEG"]) で形式を絞っても防げません。条件は、リンクした libjpeg が可逆圧縮のデコードに対応していること(libjpeg-turbo 3.0 以上)で、公式の wheel はこれを同梱しています。検証環境の 12.3.0 の wheel には libjpeg-turbo 3.1.4.1 が入っていました。
アドバイザリは、thumbnail((256,256)) を既定の reducing_gap=2.0 で呼ぶ場合、縦横とも1024ピクセル以上の画像が対象になるとしています。すぐに上げられない場合の回避策として、thumbnail() の前に img.load() を呼ぶか、thumbnail(..., reducing_gap=None) にする方法を挙げています。
編集部の検証環境で、無害な通常の JPEG(2000×1500)を使い、draft() の呼ばれ方を記録しました。脆弱性を再現したものではありません。
thumbnail((256,256)) -> draft(None, (512, 512)) が 1/2 の縮小デコードを選ぶ
load() の後に thumbnail((256,256)) -> draft は呼ばれるが None(縮小デコードしない)
thumbnail((256,256), reducing_gap=None) -> draft を呼ばない
12.3.0 と 13.0.0 で結果は同じでした。回避策の2つは、どちらも縮小デコードを使わない形になります。そのぶん全体をデコードするため、大きな画像ではメモリと時間がかかります。Django の ImageField のフォーム検証は Image.open() の後に verify() を呼ぶだけで、thumbnail() は使いません。サムネイルを作る自前のコードや、利用しているパッケージの処理を確かめます。
TIFFを受け付けているか
TIFF の4件のうち3件(c6hw・q45j・462x)は、細工した TIFF を開いて読み込むだけで当たります。c6hw と q45j のアドバイザリは、サムネイルやプレビュー、変換した画像を利用者に返すサービスでは、ワーカーのプロセスのヒープのメモリ(ほかの利用者の画像のデータなど)がピクセルとして返る可能性があるとしています。q45j は「load() は何も例外を出さない」と書いており、アプリケーションは異常に気づけません。
利用者が TIFF をアップロードする想定がなくても、受け付けていないとは限りません。Pillow は拡張子ではなくファイルの中身で形式を判定します。編集部の検証環境では、Django 5.2.18 の ImageField が既定で許可する拡張子は70種類あり、tif・tiff・gif・eps・ppm・pgm も含まれていました。
すぐに上げられない場合の編集部の提案は、受け付ける形式を Image.open(file, formats=["JPEG", "PNG", "WEBP"]) のように必要なものだけに絞ることです。TIFF・GIF・EPS・PPM の経路は通らなくなりますが、上で書いたとおり JPEG の件には効きません。
GIFのフレーム、PDFへの追記、EPS、ImageMorph
残りの4件は、特定の処理を使う場合に限られます。
- GIF(GHSA-65qm): アニメーションのフレームを
seek()で順に読み、「画像が大きすぎる」例外を捕まえて同じ画像を使い続けると、約60バイトの GIF で数GB のメモリを確保させられる。Image.open()とload()だけなら当たらず、ImageSequence.Iteratorは例外を外に出す - PDF(GHSA-p4wg): 利用者の PDF に
save(..., format="PDF", append=True)で画像を追記する処理(押印・透かしなど)で、終わらない再帰や CPU・メモリの消費が起きる - EPS(GHSA-vx6w): Ghostscript を入れたサーバーで、信頼できない EPS を
load(scale=N)(N>1)で描画する - ImageMorph(GHSA-58r4): 幅0の PPM・PGM を受け付けて
ImageMorph.MorphOp.apply()をかけるとクラッシュする
GHSA-58r4 は、アドバイザリの再現コード(幅0の画像に dilation4 をかける)を別のプロセスで実行して確かめました。12.3.0 は Segmentation fault で終了し(終了コード139)、13.0.0 は例外も出さずに戻り値を返しました。プロセスごと落ちるため、1件の要求ではなくワーカー全体が止まります。
コードで確かめるときは、thumbnail(、seek(、append=True、load(scale、ImageMorph を検索すると、該当する処理を洗い出せます。
13.0.0に上げる前に確かめる互換性
リリースノートの「後方互換性のない変更」と「API の変更」から、業務システムで踏みやすいものを、12.3.0 と 13.0.0 で同じコードを動かして確かめました。
| 変更 | 12.3.0 | 13.0.0 |
|---|---|---|
| I モード(32ビット)の画像を PNG で保存 | 保存できる。値70000は65535に丸められる | OSError: cannot write mode I as PNG |
ExifTags.IFD.Makernote | 使える(37500) | AttributeError。MakerNote に変更 |
16×16 未満の画像を sizes なしで ICO 保存 | 画像のない ICO になり、開けない | 画像の大きさの ICO になる |
Image._show | ある | 削除。ImageShow.show を使う |
I モードの PNG の保存は、16ビットの PNG に丸めて保存していた処理が例外に変わります。32ビットの I モードの画像を PNG に書き出す処理があれば、リリースノートの例のとおり im.convert("I;16") を挟みます。
見た目が変わる可能性のある変更もあります。thumbnail() が既定で使う reducing_gap が、これまで無視されていた RGBA・LA の画像にも適用されるようになりました。透過 PNG のサムネイルの仕上がりが変わる可能性があります。テキストの描画も、wheel が同梱の HarfBuzz を使うようになり、自動ヒンティングに頼る文字で描画が少し変わる可能性があるとしています。TIFF で同じタグが重複している場合は、最後ではなく最初の値を使うようになりました。この3点は、検証環境では見た目を比べていません。サムネイルや帳票の画像を作るシステムでは、更新の前後で出力を並べて確かめます。
非推奨は2つで、IM 形式の読み書きが 15.0.0(2028年10月15日)で、JpegImageFile.load_djpeg が 14(2027年10月15日)で削除される予定です。
Python 3.10のままの環境は、Pythonの更新から
Python 3.10 で動くシステムは、Pillow の修正を受けるために Python 3.11 以上への更新が先に要ります。3.10 自体のセキュリティ修正も10月で終わっているため、編集部は Pillow の件をきっかけに Python の更新を計画することを勧めます。ロックファイルや制約ファイルを使っている場合の進め方は、uv 0.13 の破壊的変更と制約ファイルの記事で触れています。
Python の更新までの間は、上で挙げた回避策を組み合わせます。
thumbnail()の前にload()を呼ぶか、reducing_gap=Noneにする(GHSA-8g5g のアドバイザリの回避策)Image.open()のformatsで、受け付ける形式を必要なものに絞る(編集部の提案。TIFF・GIF・EPS・PPM の経路を通さない)- GIF のフレームの処理で
DecompressionBombErrorを捕まえて処理を続けない(GHSA-65qm の条件を避ける) - 画像の処理をウェブのプロセスから分け、メモリの上限を付けたワーカーで動かす(編集部の提案。クラッシュやメモリの枯渇が全体に広がらないようにする)
これらは Advisory Database の9件の条件に対する手当てで、リリースノートにある残りの34件は、上で触れた GHSA-654x を除いて当たる条件を確認していません。回避策で更新を先送りし続けることはできないため、あくまで更新までのつなぎとして扱います。Node.js 側で画像を処理するライブラリの脆弱性の確かめ方は、sharp の AVIF の脆弱性の記事で整理しています。
確認した範囲
2026年10月21日に、github/advisory-database の Pillow のアドバイザリ9件の JSON、python-pillow/Pillow のタグ 13.0.0 のリリースノート(docs/releasenotes/13.0.0.rst)と pyproject.toml、PyPI の Pillow の公開情報と脆弱性情報、PEP 619、Django の
ImageFieldのソースを直接取得して確認しました(資料調査)。あわせて、Linux(Ubuntu 24.04、Python 3.11.17)で Pillow 12.3.0 と 13.0.0 の公式 wheel を使い、GHSA-58r4 の再現コードの実行、thumbnail()でのdraft()の呼ばれ方、互換性の変更の4点、Django 5.2.18 の受け付ける拡張子、uv pip compileによる Python 3.10 での解決結果、pip-audit 2.10.1 の出力を確かめました(実機検証)。JPEG・TIFF・GIF・PDF・EPS の脆弱性の再現、Advisory Database にない34件の内容(GHSA-654x を除く)、Python 3.10 の処理系での動作、Windows の wheel、ディストリビューションのパッケージ、Dependabot と OSV での表示、更新前後のサムネイルの見た目の比較は確認していません。公開前の10月21日には、開発中の版(main)のリリースノート、ドキュメントのサイトの stable と latest の文書、Pillow のリポジトリのセキュリティアドバイザリと GitHub Releases(GitHub の API)、NVD の CVE-2026-112403 も確認し、GHSA-654x の再現コードを macOS 26.5.1(Python 3.12.12)で 12.3.0 と 13.0.0 の公式 wheel を使って動かしました。
Python で作った業務システムの依存の更新計画や、Python 本体の更新の進め方は、開発・AI・自動化のご相談からお問い合わせください。
参考資料
- Pillow 13.0.0 のリリースノート(python-pillow/Pillow、docs/releasenotes/13.0.0.rst)
- Pillow 13.0.0 のリリースノート(main、10月19日の追記後)
- GHSA-654x-cwwv-5j9c: Arrow export leaks stale heap memory when PILLOW_ALIGNMENT > 1(python-pillow/Pillow の security advisory)
- Pillow 13.0.0 の pyproject.toml(requires-python)
- GHSA-8g5g-8vmr-9782: Heap buffer overflow in ImagingJpegDecode(github/advisory-database)
- GHSA-c6hw-5grf-ff96: OOB read in TiffDecode.c tile and RGBA paths(github/advisory-database)
- GHSA-q45j-58j2-45rg: TiffDecode returns uninitialized memory as pixels for short JPEG strips(github/advisory-database)
- GHSA-462x-pgg6-55fp: tiled-TIFF decoder reads past tile buffer(github/advisory-database)
- GHSA-pchr-8j38-3x4p: TIFF encoder ColorMap plane-size mismatch(github/advisory-database)
- GHSA-65qm-jvmh-6pqg: GIF seek keeps rejected size after DecompressionBombError(github/advisory-database)
- GHSA-p4wg-p73j-c528: Unbounded recursion parsing a PDF page tree or trailer chain(github/advisory-database)
- GHSA-vx6w-vv9f-6gvg: EPS load(scale) lets Ghostscript render past the decompression bomb limit(github/advisory-database)
- GHSA-58r4-6rv3-gfh8: Out-of-bounds access in _imagingmorph on a zero-width image(github/advisory-database)
- Pillow — PyPI
- PEP 619 – Python 3.10 Release Schedule
- django/forms/fields.py(django/django、ImageField)








