CyberFix Note
脆弱性・CVE解説

モデルを読み込むだけでコードが動くdiffusersのtrust_remote_codeすり抜け

対象の目安: 機械学習基盤や生成AIアプリを運用する実務担当者 / 実務

ソウ攻撃・脆弱性リサーチ担当
・ 約41分で読めます
モデルを読み込むだけでコードが動くdiffusersのtrust_remote_codeすり抜け

画像生成や動画生成の実装で広く使われているHugging Faceのdiffusersライブラリに、細工されたモデルリポジトリを読み込んだだけで任意のPythonコードが実行される脆弱性が3件公表されました。CVE-2026-44827、CVE-2026-44513、CVE-2026-45804の3件です。いずれも、外部のコードを走らせてよいかどうかを利用者に確認するためのtrust_remote_codeという仕組みをすり抜けます。既定値のままDiffusionPipeline.from_pretrained("組織名/モデル名")と書いただけで成立する経路が含まれます。

モデルの配布は、重みという巨大な数値の塊を配るだけの行為に見えます。ところがdiffusersには、リポジトリ側にPythonファイルを置いておくと、それを読み込み側で実行してパイプラインの一部として使う仕組みがあります。カスタムパイプラインとカスタムコンポーネントと呼ばれる機能です。この機能があるため、モデルリポジトリの実体は「データ」ではなく「データと実行コードの混在物」です。信頼境界はここに引かれます。

trust_remote_codeは、その境界を利用者の明示的な同意で越えさせるための引数です。既定値はFalseで、リポジトリに実行対象のコードが含まれていればエラーで止まる想定でした。今回公表された3件は、この停止機構が働かない道筋が複数残っていた、という内容です。

この記事は、機械学習基盤や生成AIアプリを運用する実務担当者に向けて、GitHub Security Advisoryの3本、NVDの各エントリ、修正が入ったPull Request #13448とバグ報告のIssue #13446、リリースv0.38.0、Hugging Faceの公式ドキュメント、報告者側の技術ブログにもとづき、事実関係と対応を整理します。攻撃コードは扱いません。記述は執筆時点(2026年8月4日)に一次情報から確認できた範囲に限ります。

3件のCVEと修正版の対応関係

まず数値と版の関係を確定させます。3件はすべてhuggingface/diffusersリポジトリのGitHub Security Advisoryとして公開され、CVEはGitHub, Inc.がCNAとして採番しています。

CVEGHSA内容CVSS 3.1CWEGitHub Advisory Database公開日
CVE-2026-44827GHSA-j7w6-vpvq-j3gmNone.pyによるtrust_remote_codeバイパス8.8 High (AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H)CWE-942026-05-07
CVE-2026-44513GHSA-98h9-4798-4q5vcustom_pipelineとローカルのカスタムコンポーネント経由のバイパス8.8 High (AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H)CWE-942026-05-07
CVE-2026-45804GHSA-7wx4-6vff-v64p設定取得とリポジトリ取得の間の競合状態によるバイパス7.5 High (AV:N/AC:H/PR:N/UI:R/S:U/C:H/I:H/A:H)CWE-3672026-05-20

影響範囲はGitHub Advisory DatabaseとOSVのいずれも「diffusers(PyPI) 0.38.0未満」で、最初の修正版は0.38.0です。v0.38.0のリリースはGitHub上で2026年5月1日(UTC)、PyPIへの公開も同日です。執筆時点のPyPIの最新版は2026年7月3日公開の0.39.0です。

表の日付はGitHub Advisory Databaseへの掲載日です。日付の起点は複数あるため、資料を突き合わせるときは区別が要ります。huggingface/diffusersリポジトリのSecurity Advisoryページ上に表示される公開日は、CVE-2026-44827とCVE-2026-44513がいずれも2026年5月1日、CVE-2026-45804が2026年5月20日で、前2件は数日ずれます。各Advisoryページの見出し部分と、GitHubのAdvisory API(https://api.github.com/advisories/GHSA-j7w6-vpvq-j3gmなど)のpublished_atを比べると差分が確認できます。

NVDでの公開日はCVE-2026-44827とCVE-2026-44513が2026年5月14日、CVE-2026-45804が2026年7月15日です。3件ともCVSS 3.1の数値はCNAであるGitHubの提供値で、NVDが独自に付けるCVSS 4.0の評価は執筆時点で掲載されていません。

GHSA-98h9-4798-4q5v(CVE-2026-44513)は、trust_remote_code=Falseを渡していても、あるいは既定値のまま省略していても、DiffusionPipeline.from_pretrained()経由で任意のリモートコードが実行されうると述べています。3つの変種が共通の根本原因を持ち、trust_remote_codeの判定がDiffusionPipeline.download()の中に実装されていたことがその原因だとしています。

CVSSベクトルのうち、UI:R(利用者の関与が必要)が付いている点は読み方に注意が要ります。ここでの関与は「怪しい操作をわざわざする」という意味ではなく、「対象のモデルリポジトリを読み込む処理を実行する」ことを指します。既定の引数だけで成立する経路がある以上、実務上の障壁としては低いものです。

深刻度スコアの各項目の意味と、環境条件を織り込んだ読み方は次の記事で整理しています。

あわせて読みたい

脆弱性対応の優先順位付け。CVSSだけに頼らないEPSSとCISA KEVの使い方

trust_remote_codeが守るはずだった境界

diffusersのカスタムパイプラインは、from_pretrained()custom_pipelineという引数を渡すと有効になります。Hub上のリポジトリを指定する場合、そのリポジトリにはpipeline.pyという名前のファイルが必要で、読み込み側はこれをimportしてパイプラインクラスとして使います。GitHub上のコミュニティパイプライン集を名前で指定する使い方もあります。

カスタムコンポーネントも同じ考え方です。モデルリポジトリのmodel_index.jsonで、UNetやスケジューラの実装として標準クラスの代わりに自前のPythonモジュールを指定しておくと、読み込み側でそのファイルが実行されます。公式ドキュメントが作例として案内するリポジトリ(sayakpaul/show-1-base-with-code)のmodel_index.jsonでは、unetの項目が["showone_unet_3d_condition", "ShowOneUNet3DConditionModel"]という2要素の配列になっており、モジュール名とクラス名が並びます。パイプラインのクラスそのものを差し替える場合は、同じ書き方を_class_nameに対して行います。読み込み側は配列の1つ目の要素に.pyを付けたファイルを探して読み込みます。

DiffusionPipeline.download()のパラメータ説明で、trust_remote_codeは「Hub上に独自のファイルとして定義されたカスタムパイプラインとカスタムコンポーネントを許可するかどうか」であり、「信頼していてコードを読んだリポジトリに対してのみTrueにすべきです。この設定はHub上に存在するコードをローカルマシン上で実行するためです」と記載されています。

公式ドキュメントは、Hubのパイプラインについて「Hugging Face Hubはファイルをスキャンしているが、それでもHubのパイプラインコードを自分で確認して安全であることを確かめるべきである」と警告しています。さらにtrust_remote_code=Trueを使う場合の追加の予防策として、コミットハッシュをrevision引数へ渡すことを強く推奨しています。悪意あるコードに差し替えられていないことを確かめるため、という理由が明記されています。

diffusersの公式ドキュメント「Community pipelines and components」は、Hubのコミュニティパイプラインがレビュー工程なしにアップロードできることを表で示したうえで、Hubがファイルをスキャンしていてもコード自体は利用者が確認すべきだと警告しています。またtrust_remote_code=Trueを使う際の追加の予防策として、revision引数へコミットハッシュを渡すことを強く推奨しています。

つまり設計上の想定は、「コードが含まれるなら止める。利用者が中身を読んで納得したときだけtrust_remote_code=Trueで通す」という一点の関門でした。今回の3件は、この関門を通らずに済む道が複数あった、という話です。

ここで区別しておきたいのが、重みファイル自体の危険性との違いです。PyTorchの重みで長く使われてきたpickle形式は、読み込むだけで任意のコードが動く形式で、safetensorsへの移行はその問題への答えでした。安全な形式の重みを使っていても、今回の経路は影響を受けます。実行されるのはリポジトリに同居する.pyファイルであって、重みの中身ではないためです。safetensorsを使っているから安心という判断は当てはまりません。

モデルやAI基盤に固有のリスクの見取り図は次の記事で扱っています。

あわせて読みたい

プロンプトインジェクションとは。AIエージェント時代の新しい攻撃と防御

ゲートの置き場所が招いたすり抜け

CVE-2026-44513は3つの変種をまとめたものです。報告のきっかけになったIssue #13446は2026年4月12日にVancir氏が提出しました。指摘の内容は、trust_remote_codeの確認が主モデルリポジトリに対してのみ行われ、custom_pipelineで指定した外部リポジトリに対しては行われていない、というものです。

0.37.1のコードを見ると構造がはっきりします。DiffusionPipeline.download()の中で、判定は次の1行に依存していました。

load_pipe_from_hub = custom_pipeline is not None and f"{custom_pipeline}.py" in filenames

filenamesは主モデルリポジトリのファイル一覧です。custom_pipelineに別のリポジトリIDを渡した場合、そのリポジトリにあるpipeline.pyは主リポジトリの一覧には現れません。条件は偽になり、trust_remote_codeの確認は行われないまま、あとの処理が外部リポジトリのpipeline.pyを取得して実行しました。

2つ目の変種は、主モデルをローカルディレクトリで指定しつつ、custom_pipelineにはHub上の別リポジトリを渡した場合です。snapshot_download()で先にダウンロードしておいたパスを渡すと、download()のHub向けの分岐そのものを通らないため、確認は一度も行われないまま外部リポジトリのコードが実行されました。3つ目の変種は、ローカルのスナップショットにカスタムコンポーネントのPythonファイルが含まれている場合です。こちらも同じ理由で確認されないまま読み込まれました。

Issue #13446は、現在の実装がtrust_remote_codeの確認を主モデルリポジトリに対してのみ行い、custom_pipelineで指定された外部リポジトリを検証していないと指摘しています。結果としてtrust_remote_code=Falseでも攻撃者が管理するリポジトリのPythonコードがダウンロードされ実行されうる、と述べています。

依存パッケージ経由でコードが持ち込まれる構図そのものは、npmエコシステムで繰り返し観測されてきたものと同じです。

あわせて読みたい

自己増殖するnpmサプライチェーンワーム。Shai-Huludとその亜種が広がる仕組みと防御

既定値のNoneがファイル名になるCVE-2026-44827

3件のうち、既定の呼び出しだけで成立する点で性質が異なるのがCVE-2026-44827です。Advisoryは発生箇所としてpipeline_loading_utils.pyの976行目を挙げています。0.37.1のソースを確認すると、その前後は次のとおりです。

def _resolve_custom_pipeline_and_cls(folder, config, custom_pipeline):
    custom_class_name = None
    if os.path.isfile(os.path.join(folder, f"{custom_pipeline}.py")):

custom_pipelineを指定しなければ値はNoneです。f文字列にNoneを埋めると文字列"None"になるため、この条件は「ダウンロード済みフォルダにNone.pyというファイルがあるか」を確かめるものに変わります。ファイル名の組み立てにPythonのオブジェクトがそのまま流し込まれた結果です。

ダウンロード側にも同じ書き方が残っていました。0.37.1のdownload()は、取得対象のパターンにf"{custom_pipeline}.py"をリポジトリのファイル一覧に含まれる場合のみ追加します。custom_pipelineNoneのときこの式は"None.py"になるため、攻撃者のリポジトリにNone.pyが置いてあれば取得対象に入り、ローカルへ落ちます。一方で先ほどの関門はcustom_pipeline is not Noneという条件のせいで偽のままです。ファイルは降りてくるのに、確認だけが飛ばされる状態でした。

GHSA-j7w6-vpvq-j3gm(CVE-2026-44827)は、custom_pipelineが既定のNoneのときファイル名"None.py"へ組み立てられ、リポジトリにこのファイルが存在するとtrust_remote_code=Trueも明示的なkwargsも要求されずに自動実行されると述べています。model_index.jsonでは標準のdiffusersクラスを使っているように見せかけたまま、DiffusionPipelineを継承したNone.pyで任意コードを実行できる、としています。

model_index.json側では通常のクラス名を書いたままにできるため、リポジトリの見た目は普通のモデルです。読み込み側のコードにもcustom_pipelinetrust_remote_codeという文字は現れません。報告者は、引数なしのfrom_pretrained('repo')だけで成立する静かなコード実行だと表現しています。

ファイル名やクラス名の解決に外部が影響できる形は、Pythonのpickleを含む逆シリアル化の問題と同じ構造を持ちます。

あわせて読みたい

安全でないデシリアライゼーションの対策。信頼できないデータの復元が任意コード実行につながる仕組み

設定取得とリポジトリ取得の間に生じるCVE-2026-45804

CVE-2026-45804は競合状態です。diffusersは、モデルを取得するときにHubへ2回別々のHTTPリクエストを出します。1回目はhf_hub_downloadmodel_index.jsonだけを取り、この内容に対してtrust_remote_codeの判定を行います。2回目はsnapshot_downloadでリポジトリのファイル群を取得します。

revisionを指定しない場合、2回のリクエストはそれぞれ呼び出し時点の既定ブランチのHEADを解決します。2つの解決の間に原子性の保証はありません。攻撃者が2回のリクエストの間にリポジトリを更新すれば、判定に使われた設定と実際にダウンロードされる中身が食い違います。

GHSA-7wx4-6vff-v64p(CVE-2026-45804)は、hf_hub_downloadとsnapshot_downloadがHubへの独立した2回の呼び出しであり、revisionがNoneのときは双方が呼び出し時点の既定ブランチのHEADを解決すること、両者の間に原子性の保証がないことを記載しています。判定を通す平常な設定のコミットと、_class_nameをリスト形式に変えてpipeline.pyを追加したコミットを入れ替えることで、確認済みの設定とは異なる内容が取得され、そのまま読み込まれるとしています。

Advisoryは成立条件も明記しています。キャッシュ済みの内容では成立しないため初回のダウンロードが必要であること、revisionにコミットハッシュを固定していると成立しないこと、そして時間差が短いため利用の多いリポジトリを狙って確率的に成功させる形になることです。窓の長さは資料によって数値が異なり、Advisory本文は約0.5秒、報告者側のブログは約0.3秒と記しています。いずれも局所環境での実測値として示されたものです。

条件が重なる分だけCVSSの攻撃条件はAC:H(高)と評価され、値も7.5にとどまりました。ただし緩和策としては強い含意があります。revisionにコミットハッシュを固定すればこの経路は成立しません。時間差そのものを消せる操作だからです。

0.38.0の修正が変えたもの

修正はhlky氏によるPull Request #13448「Improve trust_remote_code」です。2026年4月23日にマージされ、7ファイルで178行の追加と27行の削除が入りました。マージコミットはa37f6f8394ac2a7ee8360c3abea811efe54512b1です。

PR #13448の説明は、Issue #13446で挙がった3つの状況を再現コード付きで示したうえで、trust_remote_codeの確認を実際にカスタムモジュールを読み込む場所であるget_cached_module_fileへ移し、複数の呼び出し経路からtrust_remote_codeを渡して網羅すると述べています。リモートコードの供給元がlocal、git、hubの3種類あるため、get_cached_module_fileに3つのValueErrorを分けて追加したとも記載しています。あわせて、gitの経路はdiffusers公式のコミュニティパイプラインミラーが供給元であるため信頼できる扱いにできる、とも述べています。

方針は関門の移設です。取得処理のdownload()にあった判定を取り除き、動的モジュールを読み込む唯一の通り道であるget_cached_module_fileへ集約しました。0.38.0のコードでは、ローカルディレクトリの.pyを読む場合とHubのリポジトリから読む場合に、この関数の中でtrust_remote_codeが確認されます。diffusers公式のコミュニティパイプラインミラーから読む経路については、供給元が公式のデータセットであるため信頼できる扱いとされており、trust_remote_codeの確認は入りません。_get_pipeline_class_get_custom_pipeline_classにもtrust_remote_code引数が追加され、値が末端まで渡るようになりました。

注意したい点として、None.pyを組み立てる文字列補間そのものは0.38.0でも残っています。0.38.0の_resolve_custom_pipeline_and_clsは0.37.1と同じ実装です。つまりファイル名の解決結果としては同じNone.pyにたどり着きますが、その先の読み込み地点でtrust_remote_codeが確認されるため、既定のままでは例外で止まります。個々の入口を塞ぐのではなく、実行の直前に一つの関門を置く形です。

修正版には全面停止の手段も用意されています。DIFFUSERS_DISABLE_REMOTE_CODE環境変数に1trueといった値を設定すると、trust_remote_code=Trueが渡されても無視され、リモートコードの読み込みは常にエラーになります。真として扱われる値はutils/import_utils.pyENV_VARS_TRUE_VALUESで定義されており、大文字小文字は区別されません。この定数自体は0.37.1にも存在しますが、0.37.1で参照しているのはresolve_trust_remote_codeだけで、その呼び出し元はAutoModelとモジュラーパイプラインの2系統に限られます。DiffusionPipeline.from_pretrainedの経路では参照されないため、0.37.1でこの環境変数を設定しても今回の3件は止まりません。判定をget_cached_module_fileへ移した0.38.0で初めて、モジュール読み込みの通り道そのもので効くようになりました。

データベース側の食い違いと検知の落とし穴

依存関係スキャナで拾えるかどうかは、脆弱性データベース側の状態に左右されます。ここに執筆時点で確認できる食い違いがあります。

GitHub Advisory DatabaseのAPIでGHSA-j7w6-vpvq-j3gm(CVE-2026-44827)を取得すると、withdrawn_atに2026年5月7日05時25分32秒(UTC)が設定されています。公開されたのが同日02時24分22秒(UTC)ですから、公開から約3時間後に取り下げられた形です。同じ状態はOSVのエントリにも反映されており、要約文もCVE-2026-44513側の文言に置き換わっています。取り下げの理由そのものについて公式の説明は執筆時点で確認できませんでした。ただしAdvisory本文の末尾には、専用のCVEを割り当てるべき理由を述べた節が残っています。CVE-2026-44513は利用者が渡すcustom_pipeline引数か設定側の宣言を必要とする関門の置き場所の欠陥であるのに対し、こちらは既定値Noneがファイル名へ補間される文字列組み立ての欠陥であり、根本原因も発火条件も異なる、という主張です。重複として扱うかどうかのやり取りがあったことがうかがえます。

OSVのGHSA-j7w6-vpvq-j3gmは、別名としてCVE-2026-44827とPYSEC-2026-41を持ち、diffusers(PyPI)の0.38.0未満が影響、修正版0.38.0と記録しています。あわせてwithdrawnフィールドに2026年5月7日05時25分32秒(UTC)が設定されています。

一方でNVDのCVE-2026-44827は取り下げ状態ではなく、CVSS 8.8とCWE-94を掲載したまま公開されています。参照するデータ源によってこのCVEが見えたり見えなかったりする、ということです。実務では、単一のフィードだけで「該当なし」と結論づけないほうが安全です。幸い3件とも影響範囲は同じ0.38.0未満で、修正版も同じ0.38.0ですから、版で判断すれば取りこぼしません。

CISAのKEVカタログは2026年8月6日時点の最新版(2026.08.05、収録1661件)を確認しましたが、3件とも収録されていません。Hugging Faceやdiffusersを対象とする項目自体がありません。実環境での悪用が確認されたという公式の記載も見つかりませんでした。ただしNVDに併記されるCISA-ADPのSSVC評価を見ると、CVE-2026-44827とCVE-2026-45804はexploitationがpoc、CVE-2026-44513はnoneです。SSVCのpocは、実際の悪用が観測された状態を示すactiveとは別の区分です。KEV未収録を悪用手段が存在しないことと読み替えないほうが安全です。

依存パッケージの棚卸しとスキャナの運用は次の記事で整理しています。

あわせて読みたい

依存ライブラリ管理とSCA。SBOMで攻撃面を把握する

影響範囲の確認手順

自組織が影響を受けるかどうかは、インストールされているdiffusersの版で決まります。0.38.0未満であれば3件とも該当します。確認は実行環境ごとに行います。開発者の手元、CIのコンテナ、推論サーバー、Jupyter基盤、それぞれで版が違うことは珍しくありません。

diffusersの影響範囲を確認する手順

  1. 1

    各実行環境でpip show diffusersを実行し、Versionの値を記録する。0.38.0未満なら該当する

  2. 2

    requirements.txtやpyproject.tomlの記述だけで判断せず、実際に解決された版をpip freezeやuv pip listで確認する。上限を固定していない書き方では古い版が残っていることがある

  3. 3

    requirements.lockやpoetry.lock、uv.lockなどのロックファイルでdiffusersの固定版を確認し、リポジトリ間で食い違いがないかを見る

  4. 4

    コンテナイメージについては、稼働中のイメージに対してpip showを実行して確認する。ベースイメージが更新されていない環境が残りやすい

  5. 5

    diffusersを直接書いていなくても、他のライブラリの依存として入っていることがある。pip show diffusersのRequired-by欄で参照元を確認する

  6. 6

    SCAツールを使う場合は、GitHub Advisory Database系とNVD系の両方の結果を突き合わせる。CVE-2026-44827はデータベース側で取り下げ状態のため片方だけでは出ないことがある

  7. 7

    モデルの読み込み経路を洗い出し、from_pretrainedの呼び出し箇所とrevisionの指定有無を一覧にする

  8. 8

    利用しているモデルリポジトリの提供元を一覧化し、自組織以外が管理するものを区別する

最後の2つは更新後も残る作業です。ライブラリを直しても、実行するコードの出どころを把握していなければ、次に似た欠陥が出たときの影響評価がやり直しになります。

0.38.0への更新と当面の緩和策

対応の基本は0.38.0以上への更新です。執筆時点の最新版は0.39.0です。報告者側のブログも、0.38.0以降への即時の更新を第一の推奨として挙げています。

更新の際は互換性の確認が必要です。0.37系から0.38系へ上がるとパイプラインの追加やAPIの変更が入ります。リリースノートに変更点が列挙されているため、自組織で使っているパイプラインが該当するかを先に確認します。v0.38.0のリリースノートには、torchaoの量子化設定で文字列指定を廃止する変更(PR #13291)や、torchaoの最低バージョンを0.15.0へ引き上げる変更(PR #13355)も含まれます。影響の出方は利用しているパイプラインや量子化の構成、周辺ライブラリの版によって変わるため、検証環境で実際のモデルを読み込む回帰確認を挟んでから本番へ適用します。

すぐに更新できない場合に効く緩和は、脆弱性ごとに性質が異なります。前提として、GHSA-98h9-4798-4q5vは回避策の項で、これらは緩和策であって修正ではなく、完全な解消は0.38.0への更新だけだと明記しています。

緩和策効く範囲補足
DIFFUSERS_DISABLE_REMOTE_CODE環境変数を有効にする0.38.0以降でのリモートコード読み込み全般0.38.0以降でのみ有効です。0.37.1にも同名の定数はありますが、DiffusionPipeline.from_pretrainedの経路からは参照されないため、今回の3件に対する緩和にはなりません。更新前の緩和は社内ミラーと取り込み時の.py検査、revisionの固定に寄せます
revisionにコミットハッシュを固定するCVE-2026-45804の競合状態2回の取得が同じコミットを指すため時間差が消えます。公式ドキュメントもtrust_remote_code=True利用時の追加の予防策として推奨しています
モデルを事前に取得して社内ミラーへ置き、local_files_onlyで読むHubへの直接アクセス全般取り込み時点で検査でき、取り込み後の書き換えの影響も受けません。ローカルに置いたPythonファイルを読み込む経路は0.38.0未満では止まらないため、取り込み時の検査が前提です
推論プロセスの外向き通信を絞る実行後の外部への持ち出しコード実行そのものは防げませんが、資格情報の送出や追加ペイロードの取得を難しくします
推論プロセスを最小権限で動かす実行後の被害範囲クラウドのインスタンスロールやKubernetesのサービスアカウント権限を見直します

環境変数による全面停止が止めるのは、カスタムパイプラインだけではありません。0.38.0のget_cached_module_fileは、この環境変数が真なら関数の入口で例外を送出します。カスタムコンポーネントの読み込みも、ローカルディレクトリに置いた.pyを読む経路も、同じ関門を通るため一律に止まります。副作用がないと言えるのは、カスタムパイプラインとカスタムコンポーネントのどちらも使っていない環境です。自組織で使うモデルが標準のクラスだけで動くかどうかは、検証環境で環境変数を設定したうえで実際にfrom_pretrained()を通し、例外が出ないことを確かめてから常設に移します。コンテナで動かしているなら、イメージのビルド時点で環境変数を焼き込んでおくと、実行時の設定漏れを避けられます。

revisionのコミットハッシュ固定は、競合状態を消すだけでなく再現性の面でも効きます。ブランチ名を指定した読み込みは、上流のリポジトリが更新されるたびに中身が変わります。同じコードが昨日と今日で違うファイルを読む状態は、障害の切り分けも侵害の調査も難しくします。運用中のモデルは、ハッシュで固定して変更を意図的な作業として扱うほうが扱いやすくなります。

社内ミラーを置く方式は手間が増えますが、得られるものが多い緩和です。取り込みの窓口が1本になるため、リポジトリ内の.pyファイルの有無を機械的に確認する工程を挟めます。加えて、local_files_only=Trueで読むようにすれば、推論ホストからHubへの通信そのものを止められます。読み込み時に外部へ出ていく必要がなくなるため、ネットワーク側の制限も設計しやすくなります。

ただし、ローカルに置いたこと自体は防御になりません。CVE-2026-44513の3つ目の変種は、ローカルのスナップショットに含まれるカスタムコンポーネントのPythonファイルが読み込まれる経路です。0.38.0未満のdiffusersでは、ミラー経由で配置したファイルであっても同じように実行されます。GHSA-98h9-4798-4q5vも回避策として、ローカルスナップショットに対してfrom_pretrainedを呼ぶ前に、想定外の.pyファイルがないかをコンポーネントのサブディレクトリとルートの双方で確認するよう挙げています。有無を数えるだけでは足りず、見つかった場合は内容のレビューと承認を経てから配置する、という運用にして初めて意味を持ちます。

モデル配布をサプライチェーンとして扱う

今回の3件が示しているのは、モデルリポジトリが実行コードの配布経路でもある、という事実です。Hugging Face Hubには検査の仕組みがありますが、それが何をどこまで見ているかを把握したうえで運用を組む必要があります。

Hugging Face Hubのドキュメントは、リポジトリのすべてのファイルをマルウェアスキャナに通しており、スキャンは各コミットで実行されると記載しています。このページの本文に製品名の記載はなく、「malware scanner」の語がClamAVの公式サイトへリンクされています。少なくとも1つのファイルが安全でないと判定された場合は利用者に警告が表示されるとしています。

Hugging Face Hubのpickleスキャンのドキュメントは、ClamAVスキャンとPickle Import scanの2種類を実行していると説明したうえで、「ClamAVは危険なファイルの多くをカバーするがpickleの悪用はカバーしない」と述べています。さらに免責として「これは100パーセント確実なものではなく、何かが安全かどうかを確認するのは利用者の責任である」と明記しています。

ClamAVは、ハッシュ、本文のパターン、YARAルール、バイトコード署名といった複数の形式の定義にもとづいて既知の脅威を判定する仕組みです。対応する定義の種類は公式ドキュメントのSignaturesの章に一覧があります。Hugging Faceがどの定義群をどう運用しているかは公開資料からは確認できませんでしたが、少なくとも、攻撃者がその場で書いた数行のPythonファイルまで確実に検出できるという説明はありません。Hugging Face自身も、pickleスキャンのページで「100パーセント確実なものではない」と明記しています。pickleのimportスキャンは重みファイルの中身に対する検査であって、None.pypipeline.pyのようなテキストのPythonファイルを審査するものではありません。プラットフォーム側の検査は、リポジトリの中身が安全であることの保証ではない、という前提で組み立てます。

OWASP Gen AI Security ProjectのLLM03:2025 Supply Chainは、第三者モデルへの依存が改ざんや汚染の攻撃につながりうるとしたうえで、「現時点で公開されているモデルに強い来歴の保証は存在しない」と述べています。Model Cardもモデルの出自を保証しないとしています。対策として、検証可能な供給元の利用、署名とファイルハッシュによる第三者的な完全性確認、共同開発環境での監視と監査を挙げています。

IPAのAIセキュリティのページは、AIのセキュリティ面の課題として、サプライチェーン攻撃や漏洩、改ざんのリスク、学習モデルへの攻撃、学習データの改ざんや汚染、個人情報や営業秘密の漏洩、偽情報や誤情報を挙げています。あわせて、AI開発者やセキュリティ担当者向けに「AIセキュリティ短信」として最新のトレンドや事例を提供しています。

常設の運用として整えたい項目を並べます。

  • 各実行環境のdiffusersが0.38.0以上である
  • 本番で読み込むモデルリポジトリが一覧化され、提供元と用途が記録されている
  • from_pretrainedの呼び出しでrevisionにコミットハッシュを固定している
  • カスタムパイプラインを使わない環境でDIFFUSERS_DISABLE_REMOTE_CODEを有効にしている
  • trust_remote_code=Trueを使う箇所が限定され、対象コードのレビュー記録が残っている
  • 外部モデルを社内ミラーへ取り込む工程があり、取り込み時にリポジトリ内のPythonファイルの有無を確認し、見つかった場合は内容のレビューと承認を経ている
  • 推論プロセスの外向き通信が必要な宛先に限定されている
  • 推論プロセスが持つ資格情報が最小権限で、有効期限のある形式になっている
  • SBOMにdiffusersを含むPythonの依存が記録され、SCAの結果を定期的に確認している
  • SCAの参照元がGitHub Advisory Database系とNVD系の双方をカバーしている
  • モデルの読み込みが起きたホストのプロセス生成と外向き通信のログを取得している

モデルの取り込みを審査する工程を持つと、この種の欠陥が次に出たときに、影響範囲の特定と更新対象の優先順位付けを短い時間で進められます。逆に、開発者が思い思いにHubのIDを書ける状態のままだと、どのリポジトリを読み込んでいるかの把握から始めることになります。

外部のコードやツールを実行時に読み込む設計の危うさは、AI基盤の別の領域でも同じ形で現れています。

あわせて読みたい

MCPサーバーのセキュリティ。AIエージェントとツール接続の信頼境界

検証を行う場合の注意

再現確認を行う場合は、自組織が管理する隔離環境に限って実施します。第三者が公開しているリポジトリに対して攻撃的な検査を行うことや、許可のない環境でコードを実行させることは、関連法令や利用規約に抵触するおそれがあります。

注意

Issue #13446やAdvisoryには再現用のコード片が含まれますが、実行すると外部リポジトリのPythonコードが手元で動きます。本番の資格情報が読める環境や、社内ネットワークへ到達できる端末では実行しないでください。確認が必要な場合は、外部通信を遮断した使い捨てのコンテナで行います。

対応の記録として残しておきたいのは、更新前に該当版を使っていた期間と、その期間に読み込んだモデルリポジトリの一覧です。実環境での悪用が確認されたという公式の記載は執筆時点で見つかりませんでしたが、外部提供のモデルを既定の引数で読み込んでいた環境があったなら、プロセス生成と外向き通信のログを遡って確認する価値はあります。

まとめ

diffusersの3件は、モデルリポジトリが重みだけでなく実行コードの配布経路でもあるという前提を、実装の細部で崩していた例です。trust_remote_codeという関門は用意されていたものの、その置き場所が取得処理の中だったため、取得処理を通らない経路、ファイル名の解決が既定値で変わる経路、判定と取得の間に時間差が生じる経路の3方向から素通りできました。0.38.0の修正は、関門を実行の直前に一つだけ置くという方針の変更です。

実務としてやることは3段階です。まず各実行環境のdiffusersを0.38.0以上へ更新します。次に、revisionのコミットハッシュ固定とDIFFUSERS_DISABLE_REMOTE_CODEの設定という、次の欠陥にも効く設定を常設にします。そして、読み込むモデルリポジトリを一覧化し、外部提供のものを取り込み工程の対象にします。ライブラリの更新は都度の作業ですが、あとの2つは仕組みとして残ります。

数値と版の関係を最後に整理します。影響はdiffusers 0.38.0未満、修正は0.38.0、執筆時点の最新は0.39.0です。CVE-2026-44827とCVE-2026-44513がCVSS 8.8、CVE-2026-45804が7.5です。CISAのKEVカタログには3件とも収録がありません。CVE-2026-44827はGitHub Advisory DatabaseとOSVで取り下げ状態のため、スキャナの結果が参照元によって変わる点だけ覚えておくと確認の抜けを防げます。

GHSA-j7w6-vpvq-j3gm(CVE-2026-44827)。CVSS 3.1で8.8、CWE-94。発生箇所としてpipeline_loading_utils.pyの976行目を挙げ、修正はdiffusers 0.38.0のPR #13448であるとしています。報告者はzafidoとgal-zafranです。

GHSA-98h9-4798-4q5v(CVE-2026-44513)。CVSS 3.1で8.8、CWE-94。3つの変種が共通の根本原因を持ち、trust_remote_codeの判定がDiffusionPipeline.download()の中にあったことが原因であるとしています。報告者はVancir、修正担当はhlkyです。

GHSA-7wx4-6vff-v64p(CVE-2026-45804)。CVSS 3.1で7.5、CWE-367。hf_hub_downloadとsnapshot_downloadの間に原子性の保証がないこと、キャッシュ済みでは成立しないこと、revisionをコミットハッシュに固定すると成立しないことを記載しています。

NVDのCVE-2026-44827は、_resolve_custom_pipeline_and_clsでの文字列補間によりNoneが"None.py"になること、攻撃者がNone.pyを含むリポジトリを公開することでtrust_remote_code=Trueを要求せずに任意コード実行が成立することを記載しています。CVSS 3.1の8.8はGitHub, Inc.の提供値で、CVSS 4.0はNISTによる評価が未提供です。CISA-ADPによるSSVCの評価も併記されており、exploitationがpoc、automatableがno、technicalImpactがtotalとなっています。

diffusers v0.38.0のリリースには、変更点の一覧としてhlky氏によるPR #13448「Improve trust_remote_code」が記載されています。リリースの公開日時は2026年5月1日(UTC)です。

PyPIのdiffusersのリリース履歴では、0.37.0が2026年3月5日、0.37.1が2026年3月25日、0.38.0が2026年5月1日、0.39.0が2026年7月3日に公開されています。執筆時点の最新版は0.39.0です。

CISAのKnown Exploited Vulnerabilities Catalogを2026年8月6日時点の最新版(catalogVersion 2026.08.05、収録1661件、dateReleased 2026年8月5日)で確認したところ、CVE-2026-44827、CVE-2026-44513、CVE-2026-45804のいずれも収録されておらず、Hugging Faceやdiffusersを対象とする項目もありませんでした。

報告者であるZafran LabsのGal Zaban氏とIdo Shani氏によるブログは、3件をまとめてFaceHuggerと呼び、競合状態の窓が約0.3秒であったこと、diffusersの利用規模が月間約700万ダウンロードで1日あたり約20万件であること、None.pyのバイパスとTOCTOUのバイパスをHugging Faceへ報告したのが2026年3月19日であったことを記載しています。推奨事項として0.38.0以降への即時の更新を挙げています。

出典・参考

この記事をシェア

関連する記事