LangChain 因提示模板中的属性访问而易于受到模板注入攻击 · CVE-2025-65106 · GitHub 咨询数据库
漏洞详情
包: pip langchain-core (pip)
受影响版本:
>= 1.0.0, <= 1.0.6<= 0.3.79
已修补版本:
1.0.70.3.80
描述
背景
LangChain 的提示模板系统中存在一个模板注入漏洞,允许攻击者通过模板语法访问 Python 对象的内部属性。该漏洞影响那些在 ChatPromptTemplate 和相关提示模板类中接受不受信任的模板字符串(而不仅仅是模板变量)的应用程序。模板允许属性访问(.)和索引([]),但不允许方法调用(())。
属性访问和索引的结合使用,根据传递给模板的对象类型,可能被利用。当模板变量是简单字符串(常见情况)时,影响有限。然而,当使用 MessagesPlaceholder 与聊天消息对象时,攻击者可以遍历对象属性和字典查找(例如 __globals__)来获取敏感数据,如环境变量。
该漏洞明确要求应用程序从不受信任的来源接受模板字符串(结构),而不仅仅是模板变量(数据)。大多数应用程序要么不使用模板,要么使用硬编码模板,因此不受影响。
受影响组件:
langchain-core包- 模板格式:
- F-string 模板 (
template_format="f-string") - 漏洞已修复 - Mustache 模板 (
template_format="mustache") - 已进行防御性加固 - Jinja2 模板 (
template_format="jinja2") - 已进行防御性加固
- F-string 模板 (
影响
能够控制模板字符串(而不仅仅是模板变量)的攻击者可以:
- 通过属性遍历访问 Python 对象属性和内部属性
- 从对象内部提取敏感信息(例如
__class__,__globals__) - 根据传递给模板的对象,可能升级为更严重的攻击
攻击向量
1. F-string 模板注入 修复前:
|
|
2. Mustache 模板注入 修复前:
|
|
3. Jinja2 模板注入 修复前:
|
|
根本原因
- F-string 模板: 实现使用了 Python 的
string.Formatter().parse()来从模板字符串中提取变量名。此方法返回完整的字段表达式,包括属性访问语法:提取的名称未经过验证以确保它们是简单的标识符。因此,包含属性遍历和索引表达式(例如1 2 3 4 5from string import Formatter template = "{msg.__class__} and {x}" print([var_name for (_, var_name, _, _) in Formatter().parse(template)]) # 返回: ['msg.__class__', 'x']{obj.__class__.__name__}或{obj.method.__globals__[os]})的模板字符串被接受,并随后在格式化过程中被求值。虽然 f-string 模板不支持带()的方法调用,但它们支持[]索引,这可能允许遍历__globals__等字典来访问敏感对象。 - Mustache 模板: 根据设计,使用
getattr()作为后备机制来支持访问对象的属性(例如,访问User对象上的{{user.name}})。然而,我们决定将此功能限制为继承自 dict、list 和 tuple 类型的简单原语,作为防御性加固,因为不受信任的模板可能利用属性访问来触及__class__等任意对象的内部属性。 - Jinja2 模板: Jinja2 默认的
SandboxedEnvironment会阻止双下划线属性(例如__class__),但允许访问对象上的其他属性和方法。虽然在 LangChain 中 Jinja2 模板通常与受信任的模板字符串一起使用,但作为纵深防御措施,我们已经限制了环境,以阻止对传递给模板的对象的所有属性和方法访问。
哪些人会受影响?
高风险场景 如果您的应用程序满足以下条件,则会受影响:
- 接受来自不受信任来源(用户输入、外部 API、数据库)的模板字符串
- 基于用户提供的模式动态构建提示模板
- 允许用户自定义或创建提示模板
示例易受攻击代码:
|
|
低/无风险场景 如果满足以下条件,则不受影响:
- 模板字符串在您的应用程序代码中是硬编码的
- 模板字符串仅来自受信任的、受控的来源
- 用户只能提供模板变量的值,而不能提供模板结构本身
示例安全代码:
|
|
修复方案
F-string 模板 F-string 模板存在一个明确的漏洞,其中属性访问语法是可被利用的。我们添加了严格的验证来防止这种情况:
- 添加验证以强制要求变量名必须是有效的 Python 标识符
- 拒绝像
{obj.attr},{obj[0]}, 或{obj.__class__}这样的语法 - 只允许简单的变量名:
{variable_name}
|
|
Mustache 模板(防御性加固) 作为防御性加固,我们限制了 Mustache 模板支持的功能以减少攻击面:
- 将
getattr()后备机制替换为严格的类型检查 - 仅允许遍历到 dict、list 和 tuple 类型中
- 阻止对任意 Python 对象的属性访问
|
|
Jinja2 模板(防御性加固) 作为防御性加固,我们极大地限制了 Jinja2 模板的功能:
- 引入了
_RestrictedSandboxedEnvironment,阻止所有属性/方法访问 - 只允许从上下文字典中进行简单的变量查找
- 任何属性访问尝试都会引发
SecurityError
|
|
重要建议: 由于 Jinja2 的表达能力和完全沙箱化的难度,我们建议仅将 Jinja2 模板保留给受信任的来源使用。如果需要接受来自不受信任用户的模板字符串,请改用具有新限制的 f-string 或 mustache 模板。虽然我们已经加固了 Jinja2 的实现,但模板引擎的性质使得全面的沙箱化具有挑战性。最安全的方法是仅当您控制模板源时才使用 Jinja2 模板。
重要提醒:许多应用程序不需要提示模板。模板对于变量替换和动态逻辑(if 语句、循环、条件)很有用。但是,如果您正在构建聊天机器人或对话应用程序,通常可以直接使用消息对象(例如 HumanMessage、AIMessage、ToolMessage)而无需模板。直接构造消息可以完全避免与模板相关的安全问题。
补救措施
立即行动:
- 审核代码中任何从不受信任来源获取模板字符串的位置
- 更新到已修补版本的
langchain-core - 审查模板使用情况,确保模板结构与用户数据的分离
最佳实践:
- 考虑是否需要模板 - 许多应用程序可以直接使用消息对象(
HumanMessage、AIMessage等)而无需模板 - 仅将 Jinja2 用于受信任的来源 - 仅在您完全控制模板内容时使用 Jinja2 模板
参考
- GHSA-6qv9-48xg-fc7f
- langchain-ai/langchain@c4b6ba2
- langchain-ai/langchain@fa7789d
- https://nvd.nist.gov/vuln/detail/CVE-2025-65106
漏洞信息
- 发布日期: 2025年11月19日
- 严重程度: 高 (CVSS 分数:8.3)
- 弱点: CWE-1336 模板引擎中特殊元素的不当中和