Langflow: IDOR Vulnerability in `/api/v1/responses` Endpoint Allows Authenticated Attackers to Access Another User's Flow
Description
Summary
Insecure Direct Object Reference (IDOR) vulnerability in /api/v1/responses endpoint allows an authenticated attacker to execute any flow belonging to another user by specifying the victim's flow ID in the request.
Details
The vulnerability exists in the get_flow_by_id_or_endpoint_name helper function in `src/backend/base/langflow/helpers/flow.py` (lines 399-414).
When a flow is accessed via UUID (flow_id), the function queries the database directly without verifying if the authenticated user owns that flow:
# src/backend/base/langflow/helpers/flow.py:399-414
async def get_flow_by_id_or_endpoint_name(flow_id_or_name: str, user_id: str | UUID | None = None) -> FlowRead:
async with session_scope() as session:
try:
flow_id = UUID(flow_id_or_name)
# When using UUID, query directly WITHOUT checking user_id
flow = await session.get(Flow, flow_id) # ❌ No user_id check!
except ValueError:
endpoint_name = flow_id_or_name
stmt = select(Flow).where(Flow.endpoint_name == endpoint_name)
# Only when using endpoint_name is user_id checked
if user_id:
stmt = stmt.where(Flow.user_id == uuid_user_id)
This function is used by the /api/v1/responses endpoint (defined in `src/backend/base/langflow/api/v1/openai_responses.py:589`).
PoC (Proof of Concept)
# Attacker (user A) with API_KEY_A tries to execute victim (user B)'s flow
curl -X POST "http://localhost:7860/api/v1/responses" \
-H "x-api-key: sk-ATTACKER_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "VICTIM_FLOW_ID",
"input_value": "test",
"stream": false
}'
# Returns 200 and executes the victim's flow
Impact
Any authenticated user can: 1. Execute any flow in the system by knowing its flow ID 2. Access potentially sensitive data processed by victim's flows 3. Consume victim's resources
Fixes
Fixed in PR #12832 (fix(security): close IDOR in get_flow_by_id_or_endpoint_name), merged 2026-04-22, released in Langflow 1.9.1.
The helper normalizes user_id once and enforces ownership on both lookup branches (UUID *and* endpoint_name):
flow_id = UUID(flow_id_or_name)
flow = await session.get(Flow, flow_id)
if flow is not None and uuid_user_id is not None and flow.user_id != uuid_user_id:
flow = None # cross-user lookup falls through to the shared 404
Key points: - Cross-user lookups return 404 (not 403), so flow existence is not disclosed via a 403-vs-404 oracle. - /api/v1/responses and /api/v2/workflow pass user_id explicitly, so fixing the helper closes them directly; the /api/v1/run* routes were additionally moved from a bare Depends(get_flow_by_id_or_endpoint_name) to auth-aware wrapper dependencies (defense in depth). - A malformed user_id now fails closed (404 instead of a raw 500). - Webhook routes intentionally keep the unscoped lookup (public by design / explicit ownership check elsewhere). - Regression tests cover the cross-user UUID case and reproduce the original PoC against /api/v1/responses.
Acknowledgements
Thanks to the security researchers who responsibly disclosed this vulnerability: * @yzeirnials * @johnatzeropath * @LeftenantZero * @Zwique
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
Affected products
1- Range: <=1.9.0
Patches
Vulnerability mechanics
Root cause
"Missing ownership check in `get_flow_by_id_or_endpoint_name` when a flow is looked up by UUID allows any authenticated user to execute another user's flow."
Attack vector
An authenticated attacker sends a POST request to `/api/v1/responses` with a valid API key and places a victim's flow UUID in the `model` field of the JSON body [ref_id=1][ref_id=2]. Because the `get_flow_by_id_or_endpoint_name` helper never checks ownership when a UUID is supplied, the server executes the victim's flow and returns its output to the attacker. The same IDOR also affects the endpoint_name branch when `user_id` is not passed, which happened on routes using FastAPI's `Depends` pattern [ref_id=1].
Affected code
The vulnerability resides in the `get_flow_by_id_or_endpoint_name` helper in `src/backend/base/langflow/helpers/flow.py` (lines 399–414). When a flow is accessed by UUID, the function queries the database directly without verifying that the authenticated user owns that flow. This helper is used by the `/api/v1/responses` endpoint defined in `src/backend/base/langflow/api/v1/openai_responses.py:589` [ref_id=2].
What the fix does
The patch normalizes `user_id` once at the top of the helper and enforces ownership on both the UUID and endpoint_name lookup branches [patch_id=6633376]. After fetching a flow by UUID, the code now checks `if flow is not None and uuid_user_id is not None and flow.user_id != uuid_user_id` and sets `flow = None`, causing the shared 404 path to fire. Additionally, auth-aware wrapper dependencies (`get_flow_for_api_key_user`, `get_flow_for_current_user`) were added in `endpoints.py` so that the `/run*` routes no longer expose `user_id` as a free query parameter [ref_id=1]. Malformed `user_id` values now fail closed with 404 instead of a raw 500.
Preconditions
- authAttacker must have a valid API key for any authenticated user on the Langflow instance
- inputAttacker must know or guess the victim's flow UUID
- configThe target Langflow instance must be running version 1.9.0 or earlier
Reproduction
```bash curl -X POST "http://localhost:7860/api/v1/responses" \ -H "x-api-key: sk-ATTACKER_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "VICTIM_FLOW_ID", "input_value": "test", "stream": false }' # Returns 200 and executes the victim's flow ```
Generated on Jun 19, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
3News mentions
0No linked articles in our index yet.