Flowise CVE-2026-73487 正規表現でコードを検査すると、同じ機能の別名で抜ける

LLM アプリのローコード基盤である Flowise に、未認証の攻撃者がプロンプトインジェクションを通じてコードを注入できる脆弱性が見つかっている。CVE-2026-73487 である。CVSS 3.1 は 9.8(Critical)で、NVD の記述では 3.1.3 より前が対象、CWE は CWE-94(コード生成の不適切な制御)。2026-09-04 時点で CISA KEV には収載されていない。

この件が興味深いのは、破れ方が 「拒否リストに1つ足りなかった」ではなく「拒否リストという方法そのものが成り立たない環境だった」 という点にある。

何が起きるのか

NVD の記述はこうである。Flowise の CSV および Airtable の Agent ノードには 正規表現ベースの Python コード検証の迂回 があり、未認証の攻撃者がプロンプトインジェクションを介して悪意あるコードを注入できる。攻撃者は pd.read_json() のような ブロックされていない pandas の関数 を悪用して、データセットを外部へ持ち出す、内部サービスに対して SSRF を行う、あるいは未認証の予測 API を通じてコード実行を達成できる。

経路を分解すると3段になる。

  1. Flowise の予測 API(prediction API)に、認証なしでプロンプトを送れる
  2. そのプロンプトが LLM に届き、LLM が CSV / Airtable ノード用の Python コードを生成する
  3. 生成されたコードは実行前に正規表現で検査されるが、検査を通ってしまう

通常のコードインジェクションと違って、攻撃者はコードを直接送り込んでいない。自然言語を送って、LLM に書かせている。つまり入力検証の対象が「人間が書いたコード」から「LLM が書いたコード」へ移っているのに、検査の方法は従来のままだった、という構図である。

CVSS ベクタは AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H。権限も利用者の操作も要らない。

なぜ正規表現で止まらないのか

拒否リスト方式は、「危険なものの一覧が完全に作れる」 という前提の上に立っている。この前提が成り立つ場面はある。だが pandas のようなライブラリでは成り立たない。

理由は単純で、ファイルとネットワークに触れる入口が1つではないからだ。pd.read_csv を塞いでも、pd.read_json がある。pd.read_htmlpd.read_parquet もある。しかもこれらは名前のうえでは「読むだけ」の関数に見える。os.systemeval のように、見た目からして危ないという顔をしていない。

そのうえ、pandas の read_* 系は 引数に URL を取れる。ローカルパスを渡す想定の関数として拒否リストから漏れていても、URL を渡した瞬間にネットワークアクセスの手段になる。これが SSRF の入口になり、クラウドのメタデータエンドポイントまで届きうる。

手元で確かめられる。Python の標準ライブラリだけで、pandas が持つ「同じことをする関数」の数を数えてみる。

1
2
3
4
5
import pandas as pd

readers = sorted(n for n in dir(pd) if n.startswith("read_"))
print(len(readers))
print(readers)

実行すれば、read_ で始まる関数だけで20個前後が並ぶ。拒否リストにこれを全部書き、かつ将来の追加にも追随し続けるというのが、この方式が要求していることである。

さらに、正規表現は関数名の文字列を見ているにすぎない。Python にはその文字列を経由せずに同じ関数へ到達する書き方がいくつもある。

1
2
3
import pandas
f = getattr(pandas, "read_" + "json") # 名前が分割されていて正規表現に当たらない
g = pandas.__dict__["read_json"] # 属性アクセスの形が違う

つまり検査は2重に破れている。**(a) 塞ぐべき名前を数え上げられていない(b) 名前を書かずに到達する経路がある**。前者だけなら一覧を足せば済むが、後者があるので足しても終わらない。

拒否リストと許可リストの差を1行で見る

同じことは JavaScript でも確かめられる。「危険な名前を弾く」検査と「許可した名前だけを通す」検査を並べて、同じ入力を食わせてみる。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
const DENY = /\b(read_csv|eval|exec|os\.system)\b/;      // 拒否リスト
const ALLOW = /^\s*df\s*=\s*pd\.read_csv\([^()]*\)\s*$/; // 許可リスト(形で縛る)

const samples = [
"df = pd.read_csv('data.csv')",
"df = pd.read_json('http://169.254.169.254/latest/meta-data/')",
"f = getattr(pandas, 'read_' + 'json')",
];

for (const s of samples) {
console.log(
JSON.stringify(s),
"deny-passes:", !DENY.test(s), // true = 拒否リストを素通り
"allow-passes:", ALLOW.test(s) // true = 許可リストに合致
);
}

実行すると、2番目と3番目は deny-passes: true(拒否リストを素通り)かつ allow-passes: false(許可リストでは落ちる)になる。同じ入力に対して、2つの方式が逆の答えを返す。

差はどこから来ているか。拒否リストは「知っている悪い形」を列挙するので、列挙にない形は全部通る。許可リストは「知っている良い形」を列挙するので、列挙にない形は全部落ちる。未知のものをどちらへ倒すか、という設計判断が最初から入っている。

私はここが本質だと思っている。この CVE を「pandas の関数を1つ塞ぎ忘れた」と読むと、対策は「一覧を足す」になる。だが正しい読み方は「未知を通す側に倒した設計だった」であって、対策は「未知を落とす側へ倒す」になる。足す作業は終わらないが、倒す向きを変える作業は一度で済む。

もっとも、許可リストにも代償はある。上の ALLOWpd.read_csv の1行代入しか通さないので、正当な用途もかなり落ちる。落ちたものを人が見て個別に足す運用が要る。それを引き受けられないなら、そもそもコードを生成させて実行するという設計自体を見直したほうがいい。

対処

Flowise を 3.1.3 以上へ更新するのが本筋である。ただし1点ややこしい事情があって、NVD の記述は「3.1.3 より前」だが、CPE の適用範囲は「3.1.4 以下」になっている。この2つは食い違っている。上げた版が対象外だと断定するには、FlowiseAI の GitHub Security Advisory(GHSA-w7x8-q2gp-5cgg)で修正コミットを確認する必要がある。判断がつくまでは、入手できる最新版へ上げたうえで、以下も併せて残しておくのが安全である。

  • 未認証で叩ける予測 API を閉じる。 この欠陥は権限不要で成立するので、公開しているチャットフローのエンドポイントがそのまま攻撃面になる。API キーによる保護が掛かっていないフローが残っていないかを一覧で確認する
  • 使っていない CSV / Airtable の Agent ノードは無効化する。 問題が起きるのはこの2種類のノードの検証である
  • SSRF への備えは Flowise の外側で行う。 AWS なら IMDSv2 を必須にする、GCP / Azure ならコンテナからメタデータサーバへの egress を遮断する。Flowise のプロセスから出ていく通信を、必要な宛先だけに絞る

侵害の確認は3点を見る。①Flowise が扱っているデータソース(CSV、Airtable の資格情報)が外部へ送られた形跡が無いか。②内部サービスやクラウドのメタデータエンドポイントへのアクセスが無いか。③予測 API への未認証リクエストに、想定外のプロンプトを含むものが無いか。Flowise に登録した LLM プロバイダとデータソースの API キーは、疑いがあるなら再発行する。

横に展開して考える

この型は Flowise 固有ではない。LLM に生成させたコードやコマンドを、拒否リストで検査してから実行するという構造を持つものは全部同じ形をしている。エージェント基盤、コード実行サンドボックス、SQL を書かせて流すツール。どれも「危ない書き方を弾く」ところから始めがちで、そして対象のライブラリや言語が広いほど、弾ききれない。

自分の手元にそういう箇所があるなら、確かめる問いは1つでいい。「一覧にない書き方が来たとき、通るのか落ちるのか」。通るなら、この CVE と同じ側に立っている。

本記事は NVD の CVE-2026-73487 の記載(vulnStatus=Analyzed、CVSS 9.8 / Primary:nvd@nist.gov、最終更新 2026-09-03)と、CISA KEV カタログの収載状況を 2026-09-04 に確認して書いている。コード例は挙動の説明のために書いたもので、Flowise の実装そのものではない。