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- URI로 식별된 리소스를 서버가 반환하도록 요청하는 MCP 작업입니다.[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도 요구하며 권한이 없는 네임스페이스는 저장소 접근 전에 거부합니다. 패치는 경로 구분자와 traversal 문자열도 거부하고 읽기/쓰기/삭제 저장소 경로의 경계를 확인하며 전역 목록을 현재 사용자로 제한합니다. 유지관리자의 회귀 테스트는 다른 사용자의 플로 읽기를 거부합니다.[1], [2], [3], [5]
단계별 공격 실행 흐름
사용자가 자신의 프로젝트 MCP 엔드포인트에 인증합니다
공격자에게 Langflow 계정과 최소 하나의 프로젝트 범위 MCP 연결 접근 권한이 필요합니다. 이 취약점은 로그인을 우회하지 않습니다.[1]
클라이언트가 리소스 URI를 요청합니다
MCP resources/read 요청에는 끝부분 경로 구성요소가 플로 네임스페이스와 파일을 가리키는 URI가 포함됩니다.[1]
저장소가 네임스페이스로 읽습니다
storage_service.get_file()은 지정된 네임스페이스의 파일을 반환했습니다. 저장소에는 사용자 소유권 컨텍스트가 없었습니다.[1]
소스 코드 비교: 취약한 구현 vs 보안 패치
문서화된 코드 경로에서 가져온 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]
- ✓클라이언트의 임의 저장소 경로 또는 다운로드 URI 대신 서버가 생성한 불투명한 리소스 ID를 우선합니다.[1], [3]
- ✓디코딩 후 파일명을 검증하고 구분자와 traversal 구성요소를 거부하며 모든 읽기/쓰기/삭제에서 정규화된 경로 경계를 강제합니다.[1], [3]
- ✓
resources/list와tools/list쿼리를 인증된 사용자로 제한합니다. 식별자 열거는 권한 결함을 키울 수 있는 별도의 정보 노출입니다.[7] - ✓두 사용자 회귀 테스트를 추가합니다. 한 사용자의 프로젝트 MCP는 다른 사용자의 플로 파일과 다른 프로젝트의 플로를 거부해야 합니다.[1]
- ✓영향받은 배포의 접근 로그를 검토하고 노출 여부를 평가합니다. 권고문은 결함을 설명하지만 실제 악용이 확인됐다고 하지는 않습니다.[7], [3]
- ✓업스트림 회귀 테스트
test_handle_read_resource_denies_other_users_flow를 유지하고 현재 프로젝트 외부의 사용자 소유 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