文章总结: LangChain提示模板系统存在严重安全漏洞,允许攻击者通过模板语法访问Python对象内部敏感数据。该漏洞影响接受不可信模板字符串的应用程序,主要影响langchain-core包的1.0.0-1.0.6和≤0.3.79版本。攻击者可通过属性遍历访问__class__、__globals__等敏感属性。修复方案包括更新到1.0.7或0.3.80版本,并确保模板字符串来自可信源。建议开发者审核代码中模板字符串来源,考虑直接使用消息对象而非模板,并仅在完全控制模板内容时使用Jinja2模板。
综合评分: 95
文章分类: 漏洞分析,漏洞预警,应用安全,AI安全,代码审计
【漏洞情报】LangChain 提示模板注入漏洞详解:通过属性访问实现攻击
原创
qife
网络安全技术点滴分享
2025年12月5日 08:35
福建
漏洞详情
- 包管理器: 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″) – 防御性加固
影响
能够控制模板字符串(而不仅仅是模板变量)的攻击者可以:
- 通过属性遍历访问Python对象属性和内部属性
- 从对象内部(例如
__class__、__globals__)提取敏感信息 - 根据传递给模板的对象,可能升级为更严重的攻击
攻击向量
1. F-string 模板注入
修复前:
from langchain_core.prompts import ChatPromptTemplate
malicious_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 ChatPromptTemplatefrom langchain_core.messages import HumanMessage
msg = 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 ChatPromptTemplatefrom langchain_core.messages import HumanMessage
msg = 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
pypi模块地址:https://pypi.org/project/langchain-core/
github项目地址:https://github.com/langchain-ai/langchain.git
免责声明:
本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。
任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。
本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我。
本文转载自:网络安全技术点滴分享 qife《【漏洞情报】LangChain 提示模板注入漏洞详解:通过属性访问实现攻击》