flawopen.com/Teardowns/cve-2026-105752-vllm-harmony-cache-salt-omission
CVE-2026-105752:vLLMのcache_salt欠落
vLLM ResponsesのGPT-OSSツール継続処理ではcache_saltが欠落し、saltなしのプレフィックスキャッシュ領域が使われる可能性がありました。vLLM 0.30.0以降へ更新し、すべての継続処理でテナントごとのsaltを維持してください。[1], [5]
顧客ごとの書類が専用色札付きのロッカーに保管されていると想像してください。最初のリクエストには正しい札が付きますが、ツールが次の段階へ書類を戻すとき札が抜けます。共有棚はキャッシュヒットで推測したページ群の有無を示すことがありますが、ページそのものは渡しません。[1]
主要な概念と専門用語
Prefix caching- 同じプレフィックスのモデルkey/valueブロックを再利用する仕組み。[6]
cache_salt- 異なるsaltのキャッシュを分離するため、最初のブロックのハッシュに混ぜる値。[6]
Harmony continuation- Harmony応答後の次のモデル入力。ツール呼び出し後も含む。[1]
Membership oracle- 推測したプレフィックスの有無を示し、内容は明かさないシグナル。[1]
CWE-524- 機微情報を含むキャッシュの使用。アドバイザリの主な分類。[1]
フレームワーク対応
- このガイドで説明する弱点の CWE 参照: CWE-524
根本原因の分析 (Root Cause)
vLLMアドバイザリはPOST /v1/responsesのGPT-OSS Harmony経路を説明しています。最初の入力はrequest.cache_saltをtokens_inputへ渡しますが、組み込みまたはMCPツール呼び出し後の継続処理はsaltなしのtokens_input(token_ids)を使いました。helperはsaltが渡されたときだけ保存します。自動プレフィックスキャッシュを使う共有環境では、継続処理がsaltなしの領域を使います。影響範囲は0.30.0未満とされ、0.25.1で確認されていますが、それ以前の下限は未確定です。結果はcached_tokens_per_turnによるキャッシュ所属のシグナルで、任意のプロンプト本文の直接開示ではありません。CWE-200も影響分類に挙げられていますが、ここではCWE-524のみを主な弱点とします。[1], [3], [4]
ステップ・バイ・ステップの攻撃フロー
共有サービスが前提条件を満たす
影響バージョン、GPT-OSS Harmony Responses、プレフィックスキャッシュ、ツールが必要です。被害側はcache_saltを設定し継続処理に到達します。[1]
最初のエンジン入力にsaltが付く
最初のtokens_input呼び出しはrequest.cache_saltを含み、プレフィックスを呼び出し元のsalt付き領域に置きます。[1]
ツール継続処理でsaltが失われる
ツール結果後、継続処理は元のsaltなしでtokens_input(token_ids)を呼び、共有のsaltなし領域に入る可能性があります。[1]
キャッシュ数が所属を示す可能性がある
別テナントがそのプレフィックスを再構成できればcached_tokens_per_turnで存在を推測できますが、任意のプロンプト本文は取得できません。[1]
ソースコード比較:脆弱 vs 堅牢化
vLLMアドバイザリはPOST /v1/responsesのGPT-OSS Harmony経路を説明しています。最初の入力はrequest.cache_saltをtokens_inputへ渡しますが、組み込みまたはMCPツール呼び出し後の継続処理はsaltなしのtokens_input(token_ids)を使いました。helperはsaltが渡されたときだけ保存します。自動プレフィックスキャッシュを使う共有環境では、継続処理がsaltなしの領域を使います。影響範囲は0.30.0未満とされ、0.25.1で確認されていますが、それ以前の下限は未確定です。結果はcached_tokens_per_turnによるキャッシュ所属のシグナルで、任意のプロンプト本文の直接開示ではありません。CWE-200も影響分類に挙げられていますが、ここではCWE-524のみを主な弱点とします。 Upstream excerpts, not a literal line-by-line diff.[1], [3], [4], [7]
ソース抜粋: vLLM Responses Harmony renderer(異なるリビジョンの抜粋)
# 初回リクエスト経路では salt を使っていたが、ツール実行後の継続経路では使っていなかった。
engine_input = tokens_input(token_ids, cache_salt=request.cache_salt)
# ツールの結果を受け取った後、継続処理は次のように入力を再構築していた:
engine_input = tokens_input(token_ids)
# Harmony のレンダリングコンテキストでリクエストの salt を保持する。
cache_salt = cast(str | None, engine_input.get("cache_salt"))
engine_input = self.online_renderer.render_responses_harmony_messages(
context.messages,
cache_salt=cache_salt,
tok_params=tok_params,
)
# レンダラーはエンジン入力を作成するたびに salt を渡す。
engine_input = tokens_input(prompt["prompt_token_ids"], cache_salt=cache_salt)
エンジニアリング&システム堅牢化チェックリスト
- ✓vLLM 0.30.0以降へ更新し、実行中のバージョンを確認してください。[1], [5]
- ✓信頼されたゲートウェイで予測困難なcache_saltを生成し、各リクエストと継続処理に渡してください。[6], [1]
- ✓ツール後の各Harmony継続処理でsaltが維持され、テナントが分離されることをテストしてください。[7]
- ✓厳密な分離が必要なら、プロセスまたはデプロイ単位でテナントを分離してください。[6]
- ✓共有機能を見直し、cached_tokens_per_turn指標へのアクセスを制限してください。[1]
- ✓リスクはプレフィックス推測として説明し、任意のプロンプト開示とは表現しないでください。[1]