LangChain模板注入漏洞详解:攻击者可利用Prompt模板访问Python对象内部

本文详细分析了LangChain中存在的模板注入漏洞(CVE-2025-65106)。攻击者通过控制模板字符串,可访问Python对象的内部属性,如__class__、__globals__等,可能导致敏感信息泄露。文章涵盖了漏洞影响、攻击向量、修复方案及最佳实践。

LangChain Vulnerable to Template Injection via Attribute Access in Prompt Templates · CVE-2025-65106 · GitHub Advisory Database

严重性:高 | GitHub已审核 | 发布日期:2025年11月19日 | 更新日期:2025年11月25日

漏洞详情

pip: langchain-core

受影响版本

= 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模板注入 修复前:

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    
    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模板注入 修复前:

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    
    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模板注入 修复前:

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    
    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()从模板字符串中提取变量名。此方法返回完整的字段表达式,包括属性访问语法:
    1
    2
    3
    4
    5
    
    from 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、数据库)的模板字符串
  • 基于用户提供的模式动态构建提示模板
  • 允许用户自定义或创建提示模板

示例易受攻击代码:

1
2
3
4
5
6
7
8
9
# 用户控制模板字符串本身
user_template_string = request.json.get("template")  # 危险

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

result = prompt.invoke({"data": sensitive_object})

低/无风险场景 如果您的应用程序符合以下情况,则您不受影响

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

示例安全代码:

1
2
3
4
5
6
7
8
# 模板是硬编码的 - 用户只控制变量
prompt = ChatPromptTemplate.from_messages(
    [("human", "用户问题: {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}
1
2
3
4
5
# 修复后 - 这些在模板创建时被拒绝
ChatPromptTemplate.from_messages(
    [("human", "{msg.__class__}")],  # ValueError: 无效的变量名
    template_format="f-string"
)

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

  • getattr()后备替换为严格的类型检查
  • 只允许遍历到dict、list和tuple类型
  • 阻止对任意Python对象的属性访问
1
2
3
4
5
6
7
# 加固后 - 属性访问返回空字符串
prompt = ChatPromptTemplate.from_messages(
    [("human", "{{msg.__class__}}")],
    template_format="mustache"
)
result = prompt.invoke({"msg": HumanMessage("test")})
# 返回: "" (访问被阻止)

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

  • 引入了_RestrictedSandboxedEnvironment,阻止所有属性/方法访问
  • 只允许从上下文字典中进行简单的变量查找
  • 在任何属性访问尝试上引发SecurityError
1
2
3
4
5
6
# 加固后 - 所有属性访问都被阻止
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模板

参考资料

发布时间线:

  • eyurtsev2025年11月19日 发布至 langchain-ai/langchain
  • 发布于GitHub Advisory Database: 2025年11月20日
  • 审核: 2025年11月20日
  • 由国家漏洞数据库发布: 2025年11月21日
  • 最后更新: 2025年11月25日

严重性评分

CVSS总体评分:8.3 / 10(高)

CVSS v4 基础指标

  • 攻击向量: 网络
  • 攻击复杂度:
  • 攻击要求: 存在
  • 所需权限:
  • 用户交互:

脆弱系统影响指标

  • 机密性:
  • 完整性:
  • 可用性:

后续系统影响指标

  • 机密性:
  • 完整性:
  • 可用性:

CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:H/VI:L/VA:N/SC:N/SI:N/SA:N

EPSS分数:0.059%(第18百分位)

弱点

  • CWE-1336:模板引擎中使用的特殊元素的不当中和
    • 该产品使用模板引擎插入或处理外部影响的输入,但当引擎处理时,它没有中和或不正确地中和了可以被解释为模板表达式或其他代码指令的特殊元素或语法。

CVE ID: CVE-2025-65106 GHSA ID: GHSA-6qv9-48xg-fc7f 源代码: langchain-ai/langchain 报告者: 0xn3va

comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计