GHSA-jc38-x7x8-2xc8: PHP JWT Framework: JWSVerifier uses algorithm from unprotected header, enabling algorithm confusion attacks
Summary
JWSVerifier::getAlgorithm() in src/Library/Signature/JWSVerifier.php (line 144) merges protected and unprotected headers using PHP's spread operator:
$completeHeader = [...$signature->getProtectedHeader(), ...$signature->getHeader()];
In PHP, when spreading arrays with duplicate string keys, the last array's values take precedence. Since the unprotected header (getHeader()) is spread second, an attacker can override the integrity-protected alg parameter by placing a different value in the unprotected header.
This creates a Time-of-Check/Time-of-Use (TOCTOU) vulnerability:
1. HeaderCheckerManager validates alg from the protected header
2. JWSVerifier uses alg from the unprotected header for actual verification
The same issue exists in JWEDecrypter.php (lines 120-124) where array_merge() exhibits the same last-wins behavior for alg and enc.
Affected Code
JWSVerifier.php line 144 — Spread operator merge order allows unprotected header to override alg:
$completeHeader = [...$signature->getProtectedHeader(), ...$signature->getHeader()];
JWEDecrypter.php lines 120-124 — array_merge() with same last-wins behavior:
$completeHeader = array_merge(
$jwe->getSharedProtectedHeader(),
$jwe->getSharedHeader(),
$recipient->getHeader()
);
Attack Vectors
Vector A — Mixed key sets (HIGH probability)
If the application uses a JWKSet containing keys of different types (common in multi-tenant or federation scenarios), the JWSVerifier iterates all keys (line 86). An attacker can force a different algorithm that matches a different key in the set.
Vector B — alg ONLY in unprotected header (HIGH probability)
If alg is placed EXCLUSIVELY in the unprotected header (not in the protected header at all), HeaderCheckerManager::checkDuplicatedHeaderParameters() does NOT trigger. The JSON Flattened/General serializers allow tokens with no protected header or a protect
Details
Original advisory: https://github.com/advisories/GHSA-jc38-x7x8-2xc8
More from GitHub Security Advisories
- criticalGHSA-g936-7jqj-mwv8: TSDProxy: Internal proxy auth token forwarded to backend services enables management API …2026-07-10
- highGHSA-fpg8-7664-jc5q: melange: Incomplete package integrity verification allows data section substitution2026-07-10
- mediumGHSA-48rx-c7pg-q66r: Excon does not redact additional sensitive/risky headers when following redirects2026-07-10
- highGHSA-h4g2-xfmw-q2c9: Clauster: Non-loopback deployments can serve the dashboard unauthenticated when auth.enab…2026-07-10
- mediumGHSA-rqq5-2gf9-4w4q: Secure Headers: CSP directive injection via sandbox, plugin_types, and report_to when giv…2026-07-10