LangChain提示模板注入漏洞:CVE-2025-65106深度解析

本文详细分析了LangChain框架中一个高危的提示模板注入漏洞,该漏洞允许攻击者通过控制模板字符串访问Python对象的内部属性,可能导致敏感信息泄露。文章涵盖了漏洞原理、受影响组件、攻击向量以及官方的修复方案。

LangChain提示模板注入漏洞详解(CVE-2025-65106)

漏洞概述

一个模板注入漏洞存在于LangChain的提示模板系统中,允许攻击者通过模板语法访问Python对象的内部属性。该漏洞影响那些接受来自非受信源的模板字符串(而不仅仅是模板变量)的应用程序,主要涉及ChatPromptTemplate及相关提示模板类。

关键点

  • 模板允许属性访问(.)和索引([]),但不允许方法调用(())。
  • 当模板变量是简单字符串(常见情况)时,影响有限。
  • 然而,当使用MessagesPlaceholder并传入聊天消息对象时,攻击者可以通过对象属性和字典查找(例如__globals__)遍历至敏感数据,如环境变量。
  • 该漏洞特别要求应用程序从非受信源接受模板字符串(结构本身),而不仅仅是模板变量(数据)。

受影响组件

  • langchain-core (pip)
  • 受影响版本
    • >= 1.0.0, <= 1.0.6
    • <= 0.3.79
  • 已修复版本
    • 1.0.7
    • 0.3.80
  • 受影响的模板格式
    1. F-string模板 (template_format="f-string") - 漏洞已修复
    2. Mustache模板 (template_format="mustache") - 已进行防御性加固
    3. Jinja2模板 (template_format="jinja2") - 已进行防御性加固

漏洞影响

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

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

攻击向量示例

1. F-string 模板注入(修复前)

1
2
3
4
5
6
7
8
from langchain_core.prompts import ChatPromptTemplate

malicious_template = ChatPromptTemplate.from_messages(
    [("human", "{msg.__class__.__name__}")],
    template_format="f-string"
)
result = malicious_template.invoke({"msg": "foo", "msg.__class__.__name__": "safe_placeholder"})
# 之前返回: 'str'

2. Mustache 模板注入(加固前)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
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"

3. Jinja2 模板注入(加固前)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
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()来从模板字符串中提取变量名。此方法返回完整的字段表达式,包括属性访问语法。提取的名称未经验证以确保它们是简单的标识符。

Mustache 模板

设计上使用getattr()作为回退以支持访问对象上的属性。然而,我们决定将其限制为仅支持继承自dict、list和tuple类型的简单原语,以作为防御性加固。

Jinja2 模板

Jinja2的默认SandboxedEnvironment会阻止双下划线属性,但允许访问对象上的其他属性和方法。作为纵深防御措施,我们限制了环境,阻止所有对传递给模板的对象的属性和方法访问。

受影响用户

高风险场景

如果您的应用程序符合以下情况,则受影响:

  • 从非受信源(用户输入、外部API、数据库)接受模板字符串。
  • 基于用户提供的模式动态构建提示模板。
  • 允许用户自定义或创建提示模板。

易受攻击的代码示例

1
2
3
4
5
6
7
# 用户控制模板字符串本身
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
# 模板是硬编码的 - 用户仅控制变量
prompt = ChatPromptTemplate.from_messages(
    [("human", "User question: {question}")],  # 安全
    template_format="f-string"
)
result = prompt.invoke({"question": user_input})

修复方案

F-string 模板

添加了严格的验证以强制执行变量名必须是有效的Python标识符:

  • 拒绝语法如{obj.attr}{obj[0]}{obj.__class__}
  • 只允许简单的变量名:{variable_name}

修复后示例

1
2
3
4
ChatPromptTemplate.from_messages(
    [("human", "{msg.__class__}")],  # ValueError: Invalid variable name
    template_format="f-string"
)

Mustache 模板(防御性加固)

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

  • 用严格的类型检查替换了getattr()回退。
  • 只允许遍历到dict、list和tuple类型。
  • 阻止对任意Python对象的属性访问。

加固后示例

1
2
3
4
5
6
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
prompt = ChatPromptTemplate.from_messages(
    [("human", "{{msg.content}}")],
    template_format="jinja2"
)
# 引发 SecurityError: Access to attributes is not allowed

重要建议:由于Jinja2的表现力和完全沙盒化的难度,我们建议仅将Jinja2模板用于受信任的源。如果您需要从非受信任的用户接受模板字符串,请使用具有新限制的f-string或mustache模板。

修复建议

立即行动

  1. 审计代码:检查任何从非受信源获取模板字符串的位置。
  2. 更新版本:将langchain-core更新至已修复版本。
  3. 审查模板使用:确保模板结构和用户数据之间的分离。

最佳实践

  1. 考虑是否真的需要模板:许多应用程序可以直接使用消息对象(HumanMessageAIMessage等)而无需模板。
  2. 仅为受信任的源保留Jinja2:仅在完全控制模板内容时使用Jinja2模板。
  3. 重要提醒:许多应用程序不需要提示模板。模板对于变量替换和动态逻辑(if语句、循环、条件)很有用。然而,如果您正在构建聊天机器人或对话应用程序,通常可以直接使用消息对象而无需模板。直接的消息构造可以完全避免与模板相关的安全问题。

参考资料

  • GHSA-6qv9-48xg-fc7f
  • 提交记录:langchain-ai/langchain@c4b6ba2 和 langchain-ai/langchain@fa7789d
  • NVD漏洞详情:https://nvd.nist.gov/vuln/detail/CVE-2025-65106

漏洞评分

  • CVSS总体评分:8.3(高危)
  • EPSS评分:0.059%(第18百分位)
  • 相关弱点:CWE-1336 - 模板引擎中特殊元素的不当中和
comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计