news 2026/4/18 3:46:36

LangChain 昨天悄悄打了个安全补丁,你的 Agent 可能正在被“越狱“

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
LangChain 昨天悄悄打了个安全补丁,你的 Agent 可能正在被“越狱“

2026 年 4 月 8 日,一个普通的周三,LangChain-core 一天之内发了两个版本:0.3.84 和 1.2.28。

大部分人刷到 changelog 的时候会直接跳过——又是修 bug 的小版本。

但这次不一样。两个版本有一条一模一样的改动:sanitize prompts。CVE 编号 2026-4539。

这是安全补丁。说明在此之前,LangChain 的 PromptTemplate 存在一个真实可利用的注入漏洞。你可能已经在线上跑着用了 PromptTemplate 的 Agent。

一、问题出在哪:两行源码的故事

先说结论再展开原理。

LangChain 的 PromptTemplate 用的是 Python 的str.format_map做变量替换。这个方法有个特点:它不区分{}是你写在模板里的,还是用户输入带进来的。只要字符串里有{变量名},它就会尝试替换

类比一下:这就像餐厅有个规定"单据里括号里的都是厨师指令",结果顾客在菜名里写了个括号,厨师就真的按顾客的意思操作了。

来看 LangChain-core 0.3.83(补丁之前)的核心格式化逻辑:

# langchain_core/prompts/prompt.py(补丁前简化版) class PromptTemplate(StringPromptTemplate): def format(self, **kwargs: Any) -> str: kwargs = self._merge_partial_and_user_variables(**kwargs) return DEFAULT_FORMATTER_MAPPING[self.template_format]( self.template, **kwargs ) # DEFAULT_FORMATTER_MAPPING["f-string"] 最终调用: def format(self, template: str, **kwargs: Any) -> str: return template.format_map(kwargs) # ← 问题就在这里

就这一行:template.format_map(kwargs)

用户传进来的值,没有任何处理,直接喂给了format_map

二、攻击场景还原:从读取变量到泄露 API Key

设定:你做了一个翻译服务,模板长这样:

from langchain_core.prompts import PromptTemplate template = """ 你是一个专业翻译,请将原文翻译成英文。 规则:{rules} 原文:{user_text} """ prompt = PromptTemplate( input_variables=["rules", "user_text"], template=template )

正常请求:user_text="你好世界",一切正常。

攻击者请求:user_text="{rules}"

执行template.format_map({"rules": "保持专业语气", "user_text": "{rules}"})时,发生了两轮替换:

• 第一轮:{user_text}{rules}(用户注入的字符串)

• 第二轮:{rules}保持专业语气

原文那栏显示了 rules 的值。这还只是轻量版。

更危险的变体:

# Python 的 str.format_map 支持属性访问和下标访问 user_text = "{config.OPENAI_API_KEY}" # 如果你无意中把配置对象传进了 kwargs(线上代码里不罕见): result = prompt.format( rules="保持专业语气", user_text=user_text, config=app_config # ← 假设在某个中间件里统一注入了上下文 ) # 输出里会包含你的 OPENAI_API_KEY print(result) # 你是一个专业翻译... # 规则:保持专业语气 # 原文:sk-proj-xxxxxxxxxxxxxxxxxx str: """把用户输入里的 { 和 } 转义,阻止二次解析""" return value.replace("{", "{{").replace("}", "}}") def format(self, **kwargs: Any) -> str: kwargs = self._merge_partial_and_user_variables(**kwargs) if self.template_format == "f-string": sanitized = { k: _sanitize_value_for_fstring(str(v)) if isinstance(v, str) else v for k, v in kwargs.items() } return DEFAULT_FORMATTER_MAPPING["f-string"](self.template, **sanitized) # jinja2 路径启用 SandboxedEnvironment...

{替换成{{。在 Python f-string 语法里,{{是转义字符,最终渲染成字面量{,不会被当占位符解析。

和 SQL 参数化查询的哲学完全一致:数据和结构分离,不让数据改变结构的语义

Jinja2 路径启用沙箱:

from jinja2.sandbox import SandboxedEnvironment def jinja2_formatter_safe(template: str, **kwargs: Any) -> str: env = SandboxedEnvironment() # 限制属性访问,禁止危险方法 return env.from_string(template).render(**kwargs)

SandboxedEnvironment会阻断通过__class__.__mro__等 dunder 属性爬升到系统类的路径,大幅收窄 SSTI 的可利用面。

五、你的代码要怎么改:四个可直接执行的步骤

步骤一:马上升级

pip install --upgrade langchain-core # 目标:>= 0.3.84(v1.x 用户升到 >= 1.2.28) # 验证版本 python3 -c "import langchain_core; print(langchain_core.__version__)"

步骤二:全局搜索高风险用法

# 找所有用了 jinja2 格式的地方 grep -r 'template_format.*jinja2' --include="*.py" . # 找所有往 PromptTemplate.format() 传参的调用 grep -r '\.format(' --include="*.py" . | grep -v "^Binary"

重点关注:变量值来自用户请求、数据库查询结果、第三方 API 返回值的地方。

步骤三:纵深防御——额外的输入过滤层

升级之后 LangChain 自己会做 sanitize,但多一层纵深防御没坏处:

import re import logging def validate_prompt_input(text: str, max_length: int = 2000) -> str: """ 在用户输入进入 PromptTemplate 之前的额外校验层 升级到 0.3.84+ 后保留为纵深防御 """ if len(text) > max_length: raise ValueError(f"输入超长:{len(text)} > {max_length}") # 检测可疑的模板语法,记录日志供安全审计 if re.search(r'\{[^}]{0,100}\}', text): logging.warning(f"[SECURITY] 检测到可疑模板语法,来源IP可能需要关注:{text[:100]!r}") return text # 用法 user_input = validate_prompt_input(request.get("user_text", "")) result = prompt.format(rules="保持专业语气", user_text=user_input)

步骤四:模板结构绝对不能来自用户输入

# 危险:让用户选择模板字符串 user_template = request.get("template") prompt = PromptTemplate(template=user_template, ...) # ← 绝对不行 # 安全:用白名单选预定义模板 TEMPLATE_REGISTRY = { "translate": "请将以下文本翻译成英文:{text}", "summarize": "请总结以下内容,不超过200字:{text}", "qa": "基于以下上下文回答问题:\n上下文:{context}\n问题:{question}", } template_key = request.get("template_type") if template_key not in TEMPLATE_REGISTRY: raise ValueError(f"不支持的模板类型:{template_key}") prompt = PromptTemplate.from_template(TEMPLATE_REGISTRY[template_key])

六、这个漏洞揭示了一个更大的问题

从工程角度看,这个漏洞背后有个 AI 应用特有的系统性风险,比漏洞本身更值得关注。

传统 Web 应用的数据流相对简单:用户输入 → 验证 → 业务逻辑 → 数据库/响应。

AI 应用的数据流复杂得多,而且有个关键区别:中间环节的数据会被重新注入到下一轮的 Prompt 里

用户输入

向量数据库检索结果(来自外部文档,可能被攻击者预先污染)

注入面 → PromptTemplate.format()(补丁前)

LLM 推理 → Agent 决策

工具调用返回值(再次进入下一轮 Prompt)

注入面再次放大 → PromptTemplate.format()(多轮迭代)

最终输出 / 副作用(发邮件、写文件、调外部接口)

任何中间节点——工具返回值、RAG 检索结果——都可能携带恶意的模板语法。而且 Agent 的多轮迭代会把注入面放大。

这意味着 AI 应用的安全模型需要更深层地思考"什么是可信数据"。不仅用户输入不可信,外部工具的返回值同样不可信

本周 MCP Python SDK v1.27.0 发布,也包含安全修复——通过 MCP 工具返回的内容如果被不加过滤地塞进 Prompt,同样是注入面。这不是 LangChain 一家的问题,是整个 AI 工具链的系统性风险。

小结

把这次漏洞的核心逻辑串一遍:

根因str.format_map不区分"模板变量"和"变量值里的{}",用户输入可触发二次模板解析

影响:读取其他变量值;在有敏感对象传入时泄露配置数据;jinja2 模式下执行任意代码

修法:变量值进入格式化之前把{}转义成{{}};jinja2 启用 SandboxedEnvironment

原则:模板结构不能来自用户输入;所有进入 Prompt 的外部数据都视为不可信

接下来值得关注的方向:AI 应用的"输入信任边界"到底在哪里?Framework 层(LangChain 做)、工具调用层(开发者做)、LLM 的 system prompt 层(模型层做)——理想状态是三层都有,但目前缺乏统一的标准和最佳实践。这块会是 2026 年 AI 安全方向的热点。

如果这篇文章对你有帮助,欢迎转发给正在用 LangChain 做 AI 应用的同事。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/18 3:46:06

P13 | 异步任务:后台长时间操作的最佳实践

P13 | 异步任务:后台长时间操作的最佳实践 💰 付费文章 | 第二阶段:后端开发 为什么需要异步任务? 有些操作耗时很长,不适合同步等待: 操作 耗时 是否需要异步 查询列表 < 200ms ❌ 上传单张图片 1-3s ⚠️ 可选 批量生成 ZIP 打包下载 10s-5min ✅ 必须 AI 人脸识别…

作者头像 李华
网站建设 2026/4/18 3:42:30

c++怎么获取文件的Inode节点信息_stat结构体深度解析【详解】

调用 stat() 或 lstat() 填充 struct stat 后&#xff0c;st_ino 成员即为 inode 号&#xff1b;需配合 st_dev 判定唯一性&#xff0c;跨文件系统或无 inode 文件系统&#xff08;如 FAT32&#xff09;中不可靠。怎么用 stat() 拿到文件的 inode 号直接调用 stat() 函数&#…

作者头像 李华
网站建设 2026/4/18 3:42:19

Mind+学习和项目栈1

提示&#xff1a;本内容仅供自己学习使用&#xff0c;以免长时间后&#xff0c;记忆检索困难&#xff0c;特此简单梳理操作思路和具体案例。安装包啥的官网就有&#xff0c;Mind官网 - 一站式满足程序设计、模型训练、界面设计。 0.认识工具了解功能&#xff1a;我觉得没有项目…

作者头像 李华
网站建设 2026/4/18 3:40:22

PyTorch迁移学习避坑指南:修改SqueezeNet分类层时别忘了改这个隐藏参数

PyTorch迁移学习避坑指南&#xff1a;修改SqueezeNet分类层时别忘了改这个隐藏参数 在深度学习领域&#xff0c;迁移学习已经成为提升模型性能的利器。PyTorch作为当前最受欢迎的深度学习框架之一&#xff0c;其丰富的预训练模型库让开发者能够快速实现各种计算机视觉任务。然而…

作者头像 李华
网站建设 2026/4/18 3:40:11

移动通信中的线性预编码(发射端)和线性合并(接收端)算法

移动通信中的线性预编码和线性合并算法决定了基站如何精准地把信号喂给手机&#xff0c;以及手机如何从嘈杂的信号中把自己的数据抠出来。 1.发射端&#xff08;Transmitter&#xff09;的策略&#xff1a;预编码&#xff08;BF系列&#xff09; 这里的目标是&#xff1a;在信号…

作者头像 李华
网站建设 2026/4/18 3:36:15

Montgomery模乘算法详解:从数学原理到硬件优化(含CSA加法器设计)

Montgomery模乘算法详解&#xff1a;从数学原理到硬件优化&#xff08;含CSA加法器设计&#xff09; 在密码学硬件加速领域&#xff0c;模乘运算的效率直接决定了RSA、ECC等公钥密码体系的性能天花板。传统模运算中的除法操作就像高速路上的急刹车&#xff0c;而Montgomery算法…

作者头像 李华