flawopen.com/Teardowns/cve-2026-105699-langflow-mcp-cross-project-file-disclosure
CVE-2026-105699: Archivos MCP en Langflow
Langflow autenticaba la conexión MCP, pero no autorizaba cada archivo solicitado. Un usuario podía leer archivos de flujos ajenos; la versión 1.9.1 corrige la comprobación de titularidad.[1], [2], [5]
Vector 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:NImagina un hotel que comprueba que tienes una llave y después te entrega el equipaje de cualquier habitación si dices su etiqueta. La llave demuestra que puedes entrar al hotel, no que la maleta de otro huésped sea tuya. La lectura MCP de Langflow debe comprobar el propietario y el proyecto antes de abrir el archivo.[1]
Conceptos Clave y Términos
Model Context Protocol (MCP)- Protocolo por el que una aplicación de IA solicita herramientas y recursos a un servidor. El servidor debe autorizar cada objeto solicitado.[1]
Endpoint MCP ligado a un proyecto- Conexión MCP de Langflow asociada a un proyecto. Acceder al endpoint no concede acceso a archivos de todos los proyectos.[1]
resources/read- Operación MCP que solicita al servidor un recurso identificado mediante una URI.[1]
Mapeos de marcos
- Referencia CWE para la debilidad descrita en esta guía: CWE-639
Análisis de Causa Raíz
En las versiones afectadas de Langflow, de la 1.6.8 a la 1.9.0, el transporte MCP ligado a un proyecto autenticaba el acceso al ID del proyecto incluido en la URL de conexión. Después, el manejador resources/read recibía una URI, extraía un ID de flujo y un nombre de archivo y los enviaba a storage_service.get_file() sin comprobar que el flujo perteneciera al usuario autenticado o al proyecto actual. El almacenamiento lee por espacio de nombres y nombre de archivo; no conoce los permisos de usuario ni de proyecto de Langflow. Por ello, un usuario con cuenta y acceso al endpoint MCP de su propio proyecto podía solicitar un archivo del espacio de nombres de flujo de otra persona. El comportamiento global de resources/list y tools/list, documentado por separado, podía revelar identificadores y facilitar la selección del objetivo, pero no era necesario para la lectura. Es una exposición de confidencialidad autenticada entre usuarios, no acceso anónimo ni ejecución de código. El registro CVE asigna CVSS 4.0 7.1 High y CWE-639. Langflow 1.9.1 corrige el flujo: handle_read_resource() obtiene al usuario autenticado y busca el flujo exigiendo Flow.user_id == current_user.id y, en transportes de proyecto, Flow.folder_id == project_id; rechaza los espacios no autorizados antes de acceder al almacenamiento. El parche también rechaza separadores y secuencias de traversal, aplica contención en rutas de lectura/escritura/borrado y limita los listados globales al usuario actual. La prueba de regresión del mantenedor deniega la lectura del flujo de otro usuario.[1], [2], [3], [5]
Flujo de Ataque Paso a Paso
El usuario se autentica en su propio endpoint MCP de proyecto
El atacante necesita una cuenta Langflow y acceso a al menos una conexión MCP de proyecto. La vulnerabilidad no elude el inicio de sesión.[1]
El cliente solicita una URI de recurso
La solicitud resources/read incluye una URI cuyos últimos componentes identifican un espacio de nombres de flujo y un archivo.[1]
El almacenamiento lee por espacio de nombres
storage_service.get_file() devolvía el archivo del espacio indicado. El almacenamiento no tenía contexto de titularidad del usuario.[1]
Código Fuente: Vulnerable vs. Seguro
Son fragmentos enfocados en Python del flujo documentado, no una aplicación independiente. El camino vulnerable trataba el espacio de nombres enviado por el cliente como autoridad suficiente. La corrección de 1.9.1 resuelve primero el flujo dentro del usuario autenticado y, para transportes MCP de proyecto, del proyecto actual; solo entonces permite leerlo desde el almacenamiento. El parche también valida los nombres de archivo y aplica contención de rutas en la capa de almacenamiento para que otros puntos de lectura/escritura/borrado no eludan el control. Mantén la autorización en el servicio que conoce al usuario y al proyecto: un adaptador de almacenamiento no puede inferir la titularidad. Alcance del fragmento: el controlador real también permite un archivo del bucket del usuario únicamente cuando el espacio de nombres coincide con el ID del propio usuario autenticado y el servidor no tiene ámbito de proyecto. El fragmento omite esa rama independiente; no debe interpretarse como acceso a cualquier espacio de nombres. La suite de regresión upstream incluye test_handle_read_resource_denies_other_users_flow, que verifica que se deniegue la lectura entre usuarios.[1], [3], [4], [7]
Fragmento de origen: mcp_utils.py — fragmentos de código seleccionados
# Fragmento de la ruta de lectura afectada: los ID analizados se enviaban directamente al almacenamiento.
content = await storage_service.get_file(
flow_id=flow_id,
file_name=filename,
)
# Reducción con estructura de la fuente del límite de autorización de la versión 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,
)
Lista de Verificación de Seguridad para Ingeniería
- ✓Actualiza Langflow a 1.9.1 o posterior y verifica el paquete y la imagen del contenedor desplegados, no solo el código fuente.[5]
- ✓Autoriza cada URI
resources/readsegún el usuario autenticado y el proyecto de la conexión MCP antes de llamar al almacenamiento.[1], [3] - ✓Resuelve el identificador a un objeto de base de datos y exige tanto la propiedad del usuario como la pertenencia al proyecto si el endpoint tiene ese ámbito.[1], [3]
- ✓Prefiere IDs opacos de recursos generados por el servidor en lugar de aceptar rutas de almacenamiento o URI de descarga arbitrarias del cliente.[1], [3]
- ✓Valida los nombres tras decodificarlos, rechaza separadores y componentes de traversal y aplica contención canónica de rutas en todas las lecturas, escrituras y eliminaciones.[1], [3]
- ✓Limita las consultas
resources/listytools/listal usuario autenticado; considera la enumeración de identificadores una exposición separada que puede amplificar fallos de autorización.[7] - ✓Añade una prueba con dos usuarios: el MCP de proyecto de uno debe denegar archivos de flujos del otro y flujos de otro proyecto.[1]
- ✓Revisa los registros de acceso de las instalaciones afectadas y evalúa una posible exposición; el aviso describe la falla, no una explotación confirmada en el mundo real.[7], [3]
- ✓Conserve la regresión upstream
test_handle_read_resource_denies_other_users_flowy añada un caso de ámbito de proyecto que demuestre el rechazo de un flujo del usuario fuera del proyecto actual.[7], [3]
Fuentes
- 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