首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >【漏洞情报】LangChain 提示模板注入漏洞详解:通过属性访问实现攻击

【漏洞情报】LangChain 提示模板注入漏洞详解:通过属性访问实现攻击

作者头像
qife122
发布2025-12-24 13:56:00
发布2025-12-24 13:56:00
60
举报

漏洞详情

  • 包管理器: pip
  • 受影响的包: langchain-core (pip)

受影响版本:

  • = 1.0.0, <= 1.0.6
  • <= 0.3.79

已修复版本:

  • 1.0.7
  • 0.3.80

描述

背景

LangChain的提示模板系统中存在一个模板注入漏洞,允许攻击者通过模板语法访问Python对象内部。该漏洞影响接受不可信模板字符串(而不仅仅是模板变量)的应用程序中的ChatPromptTemplate及相关提示模板类。模板允许属性访问(.)和索引([]),但不允许方法调用(())。属性访问和索引的组合可能实现利用,具体取决于传递给模板的对象类型。当模板变量是简单的字符串(常见情况)时,影响有限。然而,当使用MessagesPlaceholder与聊天消息对象时,攻击者可以通过遍历对象属性和字典查找(例如__globals__)来访问敏感数据,如环境变量。

该漏洞特别要求应用程序接受来自不可信源的模板字符串(结构),而不仅仅是模板变量(数据)。大多数应用程序要么不使用模板,要么使用硬编码模板,因此不受影响。

受影响组件
  1. langchain-core包
  2. 模板格式: F-string模板 (template_format="f-string") - 漏洞已修复 Mustache模板 (template_format="mustache") - 防御性加固 Jinja2模板 (template_format="jinja2") - 防御性加固
影响

能够控制模板字符串(而不仅仅是模板变量)的攻击者可以:

  • 通过属性遍历访问Python对象属性和内部属性
  • 从对象内部(例如__class____globals__)提取敏感信息
  • 根据传递给模板的对象,可能升级为更严重的攻击
攻击向量
1. F-string 模板注入

修复前:

代码语言:javascript
复制
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 模板注入

修复前:

代码语言:javascript
复制
from langchain_core.prompts import ChatPromptTemplate
from 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 模板注入

修复前:

代码语言:javascript
复制
from langchain_core.prompts import ChatPromptTemplate
from 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、数据库)的模板字符串
  • 根据用户提供的模式动态构建提示模板
  • 允许用户自定义或创建提示模板

易受攻击的代码示例:

代码语言:javascript
复制
# 用户控制模板字符串本身
user_template_string = request.json.get("template")  # 危险

prompt = ChatPromptTemplate.from_messages(
    [("human", user_template_string)],
    template_format="mustache"
)

result = prompt.invoke({"data": sensitive_object})
低风险/无风险场景

如果符合以下情况,则不受影响

  • 模板字符串在您的应用程序代码中是硬编码的
  • 模板字符串仅来自受信任、可控的来源
  • 用户只能提供模板变量的值,而不能控制模板结构本身

安全代码示例:

代码语言:javascript
复制
# 模板是硬编码的 - 用户只控制变量
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}
代码语言:javascript
复制
# 修复后 - 这些在模板创建时被拒绝
ChatPromptTemplate.from_messages(
    [("human", "{msg.__class__}")],  # ValueError: 无效的变量名
    template_format="f-string"
)
Mustache 模板(防御性加固)

作为防御性加固,我们限制了Mustache模板的支持范围以减少攻击面:

  • 用严格的类型检查替换了getattr()后备方案
  • 只允许遍历到dictlisttuple类型
  • 阻止对任意Python对象的属性访问
代码语言:javascript
复制
# 加固后 - 属性访问返回空字符串
prompt = ChatPromptTemplate.from_messages(
    [("human", "{{msg.__class__}}")],
    template_format="mustache"
)
result = prompt.invoke({"msg": HumanMessage("test")})
# 返回: "" (访问被阻止)
Jinja2 模板(防御性加固)

作为防御性加固,我们显著限制了Jinja2模板的能力:

  • 引入了_RestrictedSandboxedEnvironment,阻止所有属性/方法访问
  • 只允许从上下文字典中进行简单的变量查找
  • 在任何属性访问尝试上引发SecurityError
代码语言:javascript
复制
# 加固后 - 所有属性访问被阻止
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

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2025-12-04,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 网络安全技术点滴分享 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 漏洞详情
  • 描述
    • 背景
    • 受影响组件
    • 影响
    • 攻击向量
      • 1. F-string 模板注入
      • 2. Mustache 模板注入
      • 3. Jinja2 模板注入
    • 根本原因
  • 谁受影响?
    • 高风险场景
    • 低风险/无风险场景
  • 修复方案
    • Mustache 模板(防御性加固)
    • Jinja2 模板(防御性加固)
  • 补救措施
    • 立即行动
    • 最佳实践
  • 参考资料
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档