flawopen.com/Teardowns/cve-2026-105699-langflow-mcp-cross-project-file-disclosure
CVE-2026-105699:Langflow MCP 文件越权读取
Langflow 验证了 MCP 连接,却没有逐个授权请求的文件。用户可读取他人流程文件;1.9.1 修复了所有权检查。[1], [2], [5]
CVSS 向量
CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N想象酒店确认你有房卡后,只要报出行李牌就把任意房间的行李交给你。房卡只能证明你能进入酒店,不能证明别人的行李属于你。Langflow 的 MCP 读取必须先检查文件所有者和项目,再打开文件。[1]
核心概念与专有名词
Model Context Protocol(MCP)- AI 应用向服务器请求工具和资源的协议。服务器仍须对每个请求对象进行授权。[1]
项目范围 MCP 端点- 与一个项目关联的 Langflow MCP 连接。能访问该端点不代表能访问所有项目的文件。[1]
resources/read- MCP 操作,要求服务器返回 URI 标识的资源。[1]
框架映射
- 本指南所述弱点的 CWE 参考: CWE-639
根本原因剖析 (Root Cause)
在受影响的 Langflow 1.6.8 至 1.9.0 中,项目范围 MCP 传输只验证连接 URL 中的项目 ID。随后 resources/read 处理器接收资源 URI,解析出流程 ID 和文件名,并将它们传给 storage_service.get_file(),却没有检查流程是否属于当前已认证用户或当前项目。存储层按命名空间和文件名读取,不了解 Langflow 的用户或项目权限。因此,拥有账户且能访问自己项目 MCP 端点的用户,可以请求其他用户流程命名空间中的文件。另行记录的全局 resources/list 和 tools/list 行为可能暴露标识符、方便定位目标,但读取漏洞并不依赖枚举。它是需要认证的跨用户机密性问题,并非匿名访问或代码执行。CVE 记录给出的 CVSS 4.0 为 7.1 High,弱点为 CWE-639。Langflow 1.9.1 修复了该路径:handle_read_resource() 获取已认证用户,并要求查询同时满足 Flow.user_id == current_user.id;项目范围传输还要求 Flow.folder_id == project_id。未授权命名空间会在访问存储前被拒绝。补丁还拒绝路径分隔符和遍历序列,在读、写、删存储路径上实施目录边界检查,并把全局列表限制到当前用户。维护者的回归测试会拒绝读取其他用户的流程。[1], [2], [3], [5]
攻击执行流程分解
源代码对比:漏洞与安全实现
这些是文档所述 Python 代码路径的重点片段,不是独立应用。存在缺陷的路径把客户端提供的命名空间当成充分权限。1.9.1 修复先在已认证用户范围内查找流程;对于项目范围 MCP 连接,还会限定当前项目,然后才允许存储层读取。补丁也会校验文件名,并在存储层强制执行路径边界,避免其他读、写、删入口绕过检查。授权应放在同时了解用户和项目的服务层;存储适配器无法自行推断所有权。 代码片段的范围:真实处理器还允许读取用户桶中的文件,但仅当命名空间等于当前已认证用户自己的 ID,且服务器不是项目范围时。片段省略了这一独立分支;不能据此推导出可访问任意命名空间。上游回归测试包含 test_handle_read_resource_denies_other_users_flow,用于断言跨用户读取会被拒绝。[1], [3], [4], [7]
源码摘录: mcp_utils.py — 对应源码片段
# 受影响读取路径的代码片段:解析出的 ID 被直接传给存储层。
content = await storage_service.get_file(
flow_id=flow_id,
file_name=filename,
)
# 按源码结构简化展示 1.9.1 版本的授权边界。
current_user = current_user_ctx.get()
query = select(Flow).where(
Flow.id == namespace_id,
Flow.user_id == current_user.id,
)
if project_id is not None:
query = query.where(Flow.folder_id == project_id)
flow = (await session.exec(query)).first()
if flow is None:
raise ValueError("Resource not found or access denied")
content = await storage_service.get_file(
flow_id=namespace_id,
file_name=filename,
)
工程与系统安全加固清单
- ✓升级到 Langflow 1.9.1 或更高版本,并核实实际部署的软件包和容器镜像,而不只是源码版本。[5]
- ✓调用存储前,按已认证用户和 MCP 连接所属项目逐个授权
resources/readURI。[1], [3] - ✓将资源标识解析为数据库对象;要求对象属于当前用户,项目范围端点还必须属于当前项目。[1], [3]
- ✓优先使用服务器生成的不透明资源 ID,不要接受客户端任意指定的存储路径或下载 URI。[1], [3]
- ✓解码后校验文件名,拒绝分隔符和路径遍历组件,并在存储层对所有读、写、删操作执行规范化路径包含检查。[1], [3]
- ✓将
resources/list与tools/list查询限制为已认证用户;标识符枚举是独立的信息泄露,可能放大授权缺陷。[7] - ✓添加双用户回归测试:一个用户的项目 MCP 端点必须拒绝读取另一个用户的流程文件及其他项目的流程。[1]
- ✓检查受影响部署的访问日志并评估潜在泄露;公告证明漏洞存在,但没有证明它已在野外被利用。[7], [3]
- ✓保留上游回归测试
test_handle_read_resource_denies_other_users_flow,并增加项目范围测试,确认当前项目之外的用户自有流程也会被拒绝。[7], [3]
参考来源
- Langflow maintainer advisory GHSA-4hmc-cfm3-w43c
- CVE Program record CVE-2026-105699
- Upstream fix commit b8fe970
- Upstream pull request #12818 and regression coverage
- Langflow 1.9.1 release
- MITRE CWE-639 — Authorization Bypass Through User-Controlled Key
- Langflow regression tests — MCP resource authorization