LangChain Vulnerable to Template Injection via Attribute Access in Prompt Templates · CVE-2025-65106 · GitHub Advisory Database
漏洞详情
包管理器: pip
受影响的包: langchain-core (pip)
受影响版本:
-
= 1.0.0, <= 1.0.6
- <= 0.3.79
已修复版本:
- 1.0.7
- 0.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 模板注入
修复前:
from langchain_core.prompts import ChatPromptTemplatemalicious_template = ChatPromptTemplate.from_messages([("human", "{msg.__class__.__name__}")],template_format="f-string"
)# 注意,这需要为 "msg.__class__.__name__" 传递一个占位符变量。
result = malicious_template.invoke({"msg": "foo", "msg.__class__.__name__": "safe_placeholder"})
# 之前返回
# >>> result.messages[0].content
# >>> 'str'
2. Mustache 模板注入
修复前:
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.messages import HumanMessagemsg = HumanMessage("Hello")# 攻击者控制模板字符串
malicious_template = ChatPromptTemplate.from_messages([("human", "{{question.__class__.__name__}}")],template_format="mustache"
)result = malicious_template.invoke({"question": msg})
# 之前返回: "HumanMessage" (getattr() 暴露了内部属性)
3. Jinja2 模板注入
修复前:
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.messages import HumanMessagemsg = HumanMessage("Hello")# 攻击者控制模板字符串
malicious_template = ChatPromptTemplate.from_messages([("human", "{{question.parse_raw}}")],template_format="jinja2"
)result = malicious_template.invoke({"question": msg})
# 可以访问对象上的非双下划线属性/方法
根本原因
- F-string模板: 实现使用Python的
string.Formatter().parse()从模板字符串中提取变量名。该方法返回完整的字段表达式,包括属性访问语法。提取的名称没有经过验证以确保它们是简单的标识符。因此,包含属性遍历和索引表达式(例如{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、数据库)的模板字符串
- 根据用户提供的模式动态构建提示模板
- 允许用户自定义或创建提示模板
易受攻击的代码示例:
# 用户控制模板字符串本身
user_template_string = request.json.get("template") # 危险prompt = ChatPromptTemplate.from_messages([("human", user_template_string)],template_format="mustache"
)result = prompt.invoke({"data": sensitive_object})
低风险/无风险场景
如果符合以下情况,则不受影响:
- 模板字符串在您的应用程序代码中是硬编码的
- 模板字符串仅来自受信任、可控的来源
- 用户只能提供模板变量的值,而不能控制模板结构本身
安全代码示例:
# 模板是硬编码的 - 用户只控制变量
prompt = ChatPromptTemplate.from_messages([("human", "User question: {question}")], # 安全template_format="f-string"
)# 用户输入仅填充‘question’变量
result = prompt.invoke({"question": user_input})
修复方案
F-string 模板
F-string 模板存在一个明显的漏洞,其中属性访问语法可被利用。我们增加了严格的验证来防止这种情况:
- 添加验证以确保变量名必须是有效的Python标识符
- 拒绝诸如
{obj.attr}、{obj[0]}或{obj.__class__}的语法 - 只允许简单的变量名:
{variable_name}
# 修复后 - 这些在模板创建时被拒绝
ChatPromptTemplate.from_messages([("human", "{msg.__class__}")], # ValueError: 无效的变量名template_format="f-string"
)
Mustache 模板(防御性加固)
作为防御性加固,我们限制了Mustache模板的支持范围以减少攻击面:
- 用严格的类型检查替换了
getattr()后备方案 - 只允许遍历到
dict、list和tuple类型 - 阻止对任意Python对象的属性访问
# 加固后 - 属性访问返回空字符串
prompt = ChatPromptTemplate.from_messages([("human", "{{msg.__class__}}")],template_format="mustache"
)
result = prompt.invoke({"msg": HumanMessage("test")})
# 返回: "" (访问被阻止)
Jinja2 模板(防御性加固)
作为防御性加固,我们显著限制了Jinja2模板的能力:
- 引入了
_RestrictedSandboxedEnvironment,阻止所有属性/方法访问 - 只允许从上下文字典中进行简单的变量查找
- 在任何属性访问尝试上引发
SecurityError
# 加固后 - 所有属性访问被阻止
prompt = ChatPromptTemplate.from_messages([("human", "{{msg.content}}")],template_format="jinja2"
)
# 引发 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
更多精彩内容 请关注我的个人公众号 公众号(办公AI智能小助手)
对网络安全、黑客技术感兴趣的朋友可以关注我的安全公众号(网络安全技术点滴分享)
公众号二维码

公众号二维码
