Know every vulnerabilitybefore it knows you.
DevGuard continuously monitors your dependencies and alerts you when CVEs like this one affect your stack — with real-time threat intelligence built for developers.
GHSA-mp82-fmj6-f22v
No affected components available
Summary
The set_session_cookie_secure before_request handler in src/pyload/webui/app/__init__.py reads the X-Forwarded-Proto header from any HTTP request without validating that the request originates from a trusted proxy, then mutates the global Flask configuration SESSION_COOKIE_SECURE on every request. Because pyLoad uses the multi-threaded Cheroot WSGI server (request_queue_size=512), this creates a race condition where an attacker's request can influence the Secure flag on other users' session cookies — either downgrading cookie security behind a TLS proxy or causing a session denial-of-service on plain HTTP deployments.
Details
The vulnerable code is in src/pyload/webui/app/__init__.py:75-84:
# Dynamically set SESSION_COOKIE_SECURE according to the value of X-Forwarded-Proto
# TODO: Add trusted proxy check
@app.before_request
def set_session_cookie_secure():
x_forwarded_proto = flask.request.headers.get("X-Forwarded-Proto", "")
is_secure = (
x_forwarded_proto.split(',')[0].strip() == "https" or
app.config["PYLOAD_API"].get_config_value("webui", "use_ssl")
)
flask.current_app.config['SESSION_COOKIE_SECURE'] = is_secure
The root cause has two components:
-
No origin validation (CWE-346): The
X-Forwarded-Protoheader is read from any client request. This header is only trustworthy when set by a known reverse proxy. WithoutProxyFixmiddleware or a trusted proxy allowlist, any client can spoof it. The code itself acknowledges this with the TODO on line 76. -
Global state mutation in a multi-threaded server:
flask.current_app.config['SESSION_COOKIE_SECURE']is application-wide shared state. When Thread A (attacker) writesFalseto this config, Thread B (victim) may readFalsewhen Flask'ssave_session()runs in the after_request phase, producing aSet-Cookieresponse without theSecureflag.
The Cheroot WSGI server is configured with request_queue_size=512 in src/pyload/webui/webserver_thread.py:46, confirming concurrent multi-threaded request processing.
No ProxyFix or equivalent middleware is configured anywhere in the codebase (confirmed via codebase-wide search).
PoC
Attack Path 1 — Cookie Security Downgrade (behind TLS-terminating proxy, use_ssl=False):
An attacker with direct access to the backend (e.g., in a containerized/Kubernetes deployment) sends concurrent requests to keep SESSION_COOKIE_SECURE set to False:
# Attacker floods backend directly, bypassing TLS proxy
for i in $(seq 1 200); do
curl -s -H 'X-Forwarded-Proto: http' http://pyload-backend:8000/ &
done
# Meanwhile, a legitimate user behind the TLS proxy receives a session cookie
# During the race window, their Set-Cookie header lacks the Secure flag
# The cookie is then vulnerable to interception over plain HTTP
Attack Path 2 — Session Denial of Service (default plain HTTP deployment):
# Attacker causes SESSION_COOKIE_SECURE=True on a plain HTTP server
for i in $(seq 1 200); do
curl -s -H 'X-Forwarded-Proto: https' http://localhost:8000/ &
done
# Concurrent legitimate users receive Set-Cookie with Secure flag
# Browser refuses to send Secure cookies over HTTP
# Users' sessions silently break — they appear logged out
The second attack path works against the default configuration (use_ssl=False) and requires no special network position.
Impact
-
Session cookie exposure (Attack Path 1): When deployed behind a TLS-terminating proxy, an attacker can cause session cookies to be issued without the
Secureflag. If the victim's browser subsequently makes an HTTP request (e.g., via a mixed-content link or downgrade attack), the session cookie is transmitted in cleartext, enabling session hijacking. -
Session denial of service (Attack Path 2): On default plain HTTP deployments, an attacker can continuously set
SESSION_COOKIE_SECURE=True, causing browsers to refuse sending session cookies back to the server. This silently breaks all concurrent users' sessions with no user-visible error message, only a redirect to login. -
No authentication required: Both attack paths are fully unauthenticated — the
before_requesthandler fires before any auth checks.
Recommended Fix
Replace the global config mutation with per-response cookie handling, and add proxy validation:
# Option A: Set Secure flag per-response instead of mutating global config
@app.after_request
def set_session_cookie_secure(response):
# Only trust X-Forwarded-Proto if ProxyFix is configured
is_secure = app.config["PYLOAD_API"].get_config_value("webui", "use_ssl")
if 'Set-Cookie' in response.headers:
# Modify cookie flags per-response, not global config
cookies = response.headers.getlist('Set-Cookie')
response.headers.remove('Set-Cookie')
for cookie in cookies:
if is_secure and 'Secure' not in cookie:
cookie += '; Secure'
response.headers.add('Set-Cookie', cookie)
return response
# Option B (preferred): Use Werkzeug's ProxyFix with explicit trust
from werkzeug.middleware.proxy_fix import ProxyFix
# In App.__new__, before returning:
if trusted_proxy_count: # from config
app.wsgi_app = ProxyFix(app.wsgi_app, x_proto=trusted_proxy_count)
# Then set SESSION_COOKIE_SECURE once at startup based on use_ssl config,
# and let ProxyFix handle X-Forwarded-Proto transparently
At minimum, remove the before_request handler entirely and set SESSION_COOKIE_SECURE once at startup (line 130 already does this in _configure_session). The dynamic per-request adjustment is the root cause of both the spoofing and the race condition.
The vulnerability can be exploited over the network without needing physical access. It is difficult for an attacker to exploit this vulnerability and may require special conditions. An attacker does not need any special privileges or access rights. No user interaction is needed for the attacker to exploit this vulnerability. The impact is confined to the system where the vulnerability exists. There is a low impact on the confidentiality of the information. There is a low impact on the availability of the system.
Exploitation attempts have been detected. Elevated vigilance and prompt remediation are advised.
Probability that this vulnerability will be exploited in the wild within the next 30 days.
We did not find any exploit available. Neither in GitHub repositories nor in the Exploit-Database.
- CVE-2026-40594Alias
- EUVD-2026-24207Alias
Browse More
Continuously monitor your dependencies and get alerted when vulnerabilities like this one affect your stack.
Checkout DevGuard