PyJWT 2.13.0 fixes the cache-clearing failure behind CVE-2026-48524, but it does not stop PyJWKClient from making a new JWKS request for every JWT carrying an unknown kid value. For Windows-hosted Python APIs that validate bearer tokens against Microsoft Entra ID, Auth0, Okta, or another remote JSON Web Key Set provider, that distinction determines the right response: upgrade immediately, then put request controls in front of the authentication path rather than assuming the library update eliminates invalid-token refresh traffic.

Microsoft’s Security Update Guide added CVE-2026-48524 on August 8, describing an attack that depends on conditions outside an attacker’s direct control. The primary record tells a more precise story. GitHub Security Advisory GHSA-fhv5-28vv-h8m8 was published by the PyJWT project on May 21, the CVE record was published on May 28, and PyJWT 2.13.0 shipped the same day as the advisory. Microsoft’s entry is therefore a late cataloging of a third-party Python dependency issue, not a Windows security update and not a vulnerability remediated by a Windows cumulative update.

The National Vulnerability Database record assigns a CVSS 3.1 score of 3.7, rated Low, with network access, no privileges, no user interaction, and high attack complexity. That score is easy to dismiss. It should instead be read as a description of the last mile of the failure: an attacker can supply arbitrary bad token headers, but the damaging state transition requires the upstream JWKS service to fail or rate-limit requests. In the services that fit that pattern, the practical effect can be legitimate users suddenly failing authentication during an identity-provider hiccup.

Cybersecurity diagram showing JWT validation, JWKS key caching, refresh attempts, and blocked token flows.The vulnerability is a cache-wipe cascade​

PyJWT’s

PyJWKClient

obtains public signing keys from a configured JWKS endpoint. A JWT header includes a

kid

, or key ID, telling the verifier which public key to look for. The header is necessarily read before the JWT signature can be verified, which means an unauthenticated client can choose any

kid

value it wants.

In PyJWT 2.12.1 and earlier, an unknown

kid

causes

PyJWKClient.get_signing_key()

to load the cached key set, fail to find a matching key, then force a fresh request to the JWKS endpoint before trying once more. There is no built-in cooldown, rate limiter, or negative cache for key IDs that have already failed lookup. Send a stream of JWTs with invented

kid

values and the application creates a matching stream of forced JWKS refreshes.

The underlying problem becomes materially worse when that remote fetch fails. In the vulnerable code,

fetch_data()

initialized the fetched key set to

None

and wrote that value to the JWKS cache in a

finally

block. A timeout, connection error, HTTP failure, or provider-side rate limit could therefore erase a still-valid cached JWKS response.

At that point the service loses its ability to verify otherwise valid tokens from keys it had already retrieved. The next validation attempt must go back to the unavailable or throttling endpoint. This is the cascading failure described in the GitHub advisory: bad-token traffic increases refreshes; an upstream error wipes known-good cache state; legitimate authentication becomes dependent on the next successful remote fetch.

That is why the published advisory says successful exploitation cannot be accomplished entirely at will. An attacker controls the invalid

kid

traffic, but cannot guarantee that Microsoft Entra ID, Okta, Auth0, an internal identity service, or a proxy between the application and the JWKS endpoint will return an error at the needed moment. A weak upstream rate limit, an ordinary transient outage, or an already stressed provider makes the condition easier to reach.

Version 2.13.0 fixes the failure mode, not every refresh​

The PyJWT project’s 2.13.0 release notes are more exact than the CVE title. They say the fix moves the cache update into the successful-fetch path so transient errors no longer evict valid cached keys. A code comparison confirms that result: 2.12.1 writes to the cache even after a failed fetch, whereas 2.13.0 writes only after it has obtained and parsed a JWKS response.

That is a meaningful correction. An application running 2.13.0 can keep using its existing valid key set when a forced refresh fails, rather than instantly converting a provider-side problem into an application-wide authentication outage. The release also includes other security fixes, including restrictions on non-HTTP(S) JWKS URLs, making an upgrade a stronger recommendation than this CVE alone would suggest.

But the same 2.13.0 source still retains the unknown-

kid

refresh behavior. It checks the cached key set, then explicitly calls the key-loading path with

refresh=True

when it cannot find a matching key. The current PyJWT documentation also states that an absent match refreshes the set and retries once.

That means the headline phrase “unbounded JWKS endpoint requests” remains technically true after the CVE’s designated fixed version. What 2.13.0 removes is the cache-wipe condition that turns an upstream fetch failure into broader authentication unavailability. It does not introduce a refresh cooldown, reject repeated novel

kid

values before requesting the provider, or limit the rate of forced refreshes.

Administrators should not interpret this as a reason to stay on 2.12.1. The cache-preservation change is exactly what prevents the documented CVE cascade. It does mean that teams seeing persistent JWKS requests under malformed-token traffic must solve that traffic problem at the gateway, application, or identity-provider layer.

Which deployments need attention​

The affected capability is

PyJWKClient

, not every use of PyJWT. Services that use only locally configured HMAC secrets, pinned public keys, or a locally managed

PyJWKSet

do not make remote JWKS requests through this component and are outside the CVE’s practical attack path.

GitHub’s advisory identifies PyJWT 2.4.0 through 2.12.1 as the affected range because that is the span containing

PyJWKClient

. The CVE record describes the range more broadly as all versions before 2.13.0. These statements do not create a meaningful remediation conflict: the operational boundary is whether the deployed code imports and uses

PyJWKClient

against a network JWKS URI.

The Windows angle is deployment rather than platform exposure. A FastAPI, Flask, Django, Azure Functions, IIS-hosted Python service, Windows container, or scheduled service running on Windows is affected in the same way as a Linux-hosted process if it accepts untrusted bearer tokens and passes them to

get_signing_key_from_jwt()

. The library’s HTTP fetch uses Python’s networking stack; this is not a flaw in Windows, IIS, Entra ID, or the JWT format itself.

A quick inventory should look for both the installed package and the call pattern:

  • Check dependency manifests, lock files, virtual environments, and container images for PyJWT versions earlier than 2.13.0.
  • Search source code for PyJWKClient, get_signing_key_from_jwt, and get_signing_key, rather than assuming every package named jwt is PyJWT.
  • Identify the JWKS hostname used by each service, its normal response time, timeout behavior, and whether requests are subject to per-client or shared egress limits.
  • Review authentication logs for high volumes of invalid tokens with changing or nonexistent kid values, especially when they correlate with JWKS fetch errors.

Patch first, then control the trigger​

Upgrading to PyJWT 2.13.0 or later is the correct first move. It preserves a known-good JWKS cache across transient remote failures and picks up the project’s other May 2026 security work. Test the update in applications that have unusual JWT handling, particularly where key rotation is frequent, because the release also tightens several security checks beyond this CVE.

After patching, the operational safeguard is rate limiting before token verification reaches

PyJWKClient

. Apply limits to unauthenticated API routes, login-protected endpoints that parse bearer tokens before authorization, and reverse-proxy paths serving machine clients. A web application firewall or API gateway rule that drops obvious token floods is more valuable here than an application-side retry policy, since retries add pressure to the same JWKS dependency.

Teams should also watch outbound traffic from application instances to their configured JWKS URLs. A sudden one-request-per-invalid-token pattern is observable in proxy, firewall, service-mesh, or identity-provider logs. It is also a useful alert because it can signal either malicious token spraying or a client deployment issuing tokens with a stale

kid

after a key rotation.

PyJWT’s default JWKS cache lifetime is 300 seconds, but that cache does not prevent a forced refresh after every unknown key ID. Enabling PyJWKClient’s optional per-key cache helps only for successfully resolved signing keys; it does not cache failed lookups. If a service must accept high-volume public traffic, implement a bounded negative-cache or refresh-coalescing layer around remote key retrieval, while ensuring the design still allows legitimate key rotation to take effect promptly.

The immediate consequence is clear: patch PyJWT to 2.13.0 or newer to stop transient JWKS failures from flushing usable keys, then treat recurring forced JWKS refreshes as an application-edge capacity issue. Microsoft’s August 8 listing puts the CVE on Windows administrators’ radar, but no Microsoft patch exists for it; the durable remediation sits in Python dependency management and in the controls protecting the token-validation endpoint.


References​

  1. Primary source: MSRC
    Published: August 8, 2026 at 8:40 AM UTC
  2. Related coverage: github.com
  3. Related coverage: advisories.gitlab.com
  4. Related coverage: github.com
  5. Related coverage: kodemsecurity.com
  6. Related coverage: app.opencve.io
  7. Related coverage: pyjwt.readthedocs.io
  8. Related coverage: nvd.nist.gov
  9. Related coverage: sec.co
  10. Related coverage: pyjwt.readthedocs.io
  11. Related coverage: pypi.org
  12. Related coverage: packages.gentoo.org
  13. Related coverage: pypi.org