LangChain模板注入漏洞解析:通过属性访问实现攻击的技术细节

本文详细分析了LangChain框架中的模板注入漏洞CVE-2025-65106,该漏洞允许攻击者通过属性访问语法访问Python对象内部属性,可能导致敏感信息泄露。文章涵盖漏洞原理、攻击向量和修复方案。

LangChain模板注入漏洞分析

漏洞概述

LangChain的提示模板系统存在模板注入漏洞,允许攻击者通过模板语法访问Python对象内部属性。该漏洞影响接受不受信任模板字符串(不仅仅是模板变量)的ChatPromptTemplate和相关提示模板类。

受影响组件

  • 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
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
11
12
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
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']

提取的名称未经验证以确保它们是简单标识符。

Mustache模板

设计上使用getattr()作为回退,以支持访问对象上的属性。

Jinja2模板

Jinja2的默认SandboxedEnvironment阻止双下划线属性(例如__class__),但允许访问对象上的其他属性和方法。

受影响用户

高风险场景

如果您的应用程序满足以下条件,则受到影响:

  • 接受来自不受信任源的模板字符串(用户输入、外部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
ChatPromptTemplate.from_messages(
    [("human", "{msg.__class__}")],  # ValueError: 无效的变量名
    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:不允许访问属性

重要建议

由于Jinja2的表达能力和完全沙箱化的难度,我们建议仅将Jinja2模板保留给受信任的源。如果需要接受来自不受信任用户的模板字符串,请改用具有新限制的f-string或mustache模板。

修复措施

立即行动

  • 审计代码中模板字符串来自不受信任源的任何位置
  • 更新到langchain-core的修补版本
  • 审查模板使用情况,确保模板结构和用户数据分离

最佳实践

  • 考虑是否需要模板 - 许多应用程序可以直接使用消息对象(HumanMessage、AIMessage等)而无需模板
  • 将Jinja2保留给受信任的源 - 仅在完全控制模板内容时使用Jinja2模板

技术规格

  • 严重程度:高
  • CVSS评分:8.3
  • 受影响版本:>= 1.0.0, <= 1.0.6 和 <= 0.3.79
  • 修补版本:1.0.7 和 0.3.80

参考

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