
受影响版本:
已修复版本:
LangChain的提示模板系统中存在一个模板注入漏洞,允许攻击者通过模板语法访问Python对象内部。该漏洞影响接受不可信模板字符串(而不仅仅是模板变量)的应用程序中的ChatPromptTemplate及相关提示模板类。模板允许属性访问(.)和索引([]),但不允许方法调用(())。属性访问和索引的组合可能实现利用,具体取决于传递给模板的对象类型。当模板变量是简单的字符串(常见情况)时,影响有限。然而,当使用MessagesPlaceholder与聊天消息对象时,攻击者可以通过遍历对象属性和字典查找(例如__globals__)来访问敏感数据,如环境变量。
该漏洞特别要求应用程序接受来自不可信源的模板字符串(结构),而不仅仅是模板变量(数据)。大多数应用程序要么不使用模板,要么使用硬编码模板,因此不受影响。
能够控制模板字符串(而不仅仅是模板变量)的攻击者可以:
__class__、__globals__)提取敏感信息修复前:
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'修复前:
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() 暴露了内部属性)修复前:
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})
# 可以访问对象上的非双下划线属性/方法如果您的应用程序符合以下情况,则受影响:
易受攻击的代码示例:
# 用户控制模板字符串本身
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 模板存在一个明显的漏洞,其中属性访问语法可被利用。我们增加了严格的验证来防止这种情况:
# 修复后 - 这些在模板创建时被拒绝
ChatPromptTemplate.from_messages(
[("human", "{msg.__class__}")], # ValueError: 无效的变量名
template_format="f-string"
)作为防御性加固,我们限制了Mustache模板的支持范围以减少攻击面:
getattr()后备方案dict、list和tuple类型# 加固后 - 属性访问返回空字符串
prompt = ChatPromptTemplate.from_messages(
[("human", "{{msg.__class__}}")],
template_format="mustache"
)
result = prompt.invoke({"msg": HumanMessage("test")})
# 返回: "" (访问被阻止)作为防御性加固,我们显著限制了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)而无需模板。直接的消息构造完全避免了与模板相关的安全问题。
考虑是否需要模板 - 许多应用程序可以直接使用消息对象(HumanMessage、AIMessage等)而无需模板
Jinja2仅用于受信任源 - 仅在完全控制模板内容时使用Jinja2模板
pypi模块地址:https://pypi.org/project/langchain-core/
github项目地址:https://github.com/langchain-ai/langchain.git