Skip to main content
Press slash or control plus K to focus the search. Use the arrow keys to navigate results and press enter to open a threat.
Reconnecting to live updates…

Jwt library: PHP JWT Framework: JWSVerifier uses algorithm from unprotected header, enabling algorithm confusion attacks

0
High
Published: 06/18/2026 (06/18/2026, 21:09:17 UTC)
Source: GCVE Database
Product: web-token/jwt-library

Description

## Summary `JWSVerifier::getAlgorithm()` in `src/Library/Signature/JWSVerifier.php` (line 144) merges protected and unprotected headers using PHP's spread operator: ```php $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`: ```php $completeHeader = [...$signature->getProtectedHeader(), ...$signature->getHeader()]; ``` **JWEDecrypter.php lines 120-124** — `array_merge()` with same last-wins behavior: ```php $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 protected header without `alg`. RFC 7515 Section 4.1.1 states `alg` MUST be integrity-protected, but the library does not enforce this. ### Vector C — Direct JWSVerifier usage (HIGH probability) `JWSLoader` takes `?HeaderCheckerManager` (nullable). If developers use `JWSVerifier` directly or create `JWSLoader` without a `HeaderCheckerManager`, the duplicate header check never runs. ## Contrast with JWSBuilder (safe) `JWSBuilder::findSignatureAlgorithm()` (line 196) uses `[...$header, ...$protectedHeader]` where protected wins. It also has `checkDuplicatedHeaderParameters()` (line 218). The JWSVerifier has **neither** safeguard. ## Proof of Concept ```php <?php // Demonstrate algorithm override via unprotected header $protected = ["alg" => "RS256", "typ" => "JWT"]; $unprotected = ["alg" => "HS256"]; $merged = [...$protected, ...$unprotected]; // $merged["alg"] === "HS256" — unprotected wins! // JSON Flattened JWS with algorithm override: $maliciousJws = json_encode([ 'payload' => base64url_encode($payload), 'protected' => base64url_encode('{"alg":"RS256"}'), 'header' => ['alg' => 'HS256'], // OVERRIDE 'signature' => base64url_encode($sig), ]); // HeaderCheckerManager validates RS256 from protected header -> PASS // JWSVerifier uses HS256 from unprotected header -> attacker's algorithm choice ``` A full working PoC demonstrating HS512-to-HS256 downgrade with mixed keysets is available upon request. ## Suggested Fix In `JWSVerifier::getAlgorithm()`, read `alg` exclusively from the protected header: ```php private function getAlgorithm(Signature $signature): Algorithm { $protectedHeader = $signature->getProtectedHeader(); if (! isset($protectedHeader['alg'])) { throw new InvalidArgumentException('The "alg" parameter must be in the protected header.'); } return $this->signatureAlgorithmManager->get($protectedHeader['alg']); } ``` For `JWEDecrypter`, reverse the merge order so protected header wins, or extract `alg`/`enc` exclusively from the protected header. ## Résolution Un correctif a été préparé sur une branche dédiée basée sur `3.4.x`, avec des tests anti-régression dédiés (fork privé temporaire de cette advisory, PR #1). **JWS algorithm confusion** — `JWSVerifier` lit le paramètre `alg` exclusivement dans le header protégé en intégrité (RFC 7515 §4.1.1) ; un `alg` placé dans le header non protégé ne peut plus surcharger l'algorithme signé. **Validation :** `php -l` OK, PHPUnit vert, aucune nouvelle erreur PHPStan introduite (différentiel nul vs `3.4.x`), aucun commentaire ajouté dans le code source. Après merge, cascade prévue `3.4.x → 4.0.x → 4.1.x`.

CVSS v4.0

Attack Vector
Network
Attack Complexity
High
Attack Requirements
None
Privileges Required
None
User Interaction
None
Vuln. Confidentiality
High
Vuln. Integrity
High
Vuln. Availability
None
Subsq. Confidentiality
None
Subsq. Integrity
None
Subsq. Availability
None
CVSS:4.0/AV:N/AC:H/AT:N/PR:N/UI:N/VC:H/VI:H/VA:N/SC:N/SI:N/SA:N/E:P

Affected software

Packagistghsa
web-token/jwt-library
Affected versions
<3.4.10
Packagistghsa
web-token/jwt-library
Affected versions
>=4.0.0 <4.0.7
Packagistghsa
web-token/jwt-library
Affected versions
>=4.1.0 <4.1.7
Packagistghsa
web-token/jwt-bundle
Affected versions
<3.4.10
Packagistghsa
web-token/jwt-bundle
Affected versions
>=4.0.0 <4.0.7
Packagistghsa
web-token/jwt-bundle
Affected versions
>=4.1.0 <4.1.7
Packagistghsa
web-token/jwt-experimental
Affected versions
<3.4.10
Packagistghsa
web-token/jwt-experimental
Affected versions
>=4.0.0 <4.0.7
Packagistghsa
web-token/jwt-experimental
Affected versions
>=4.1.0 <4.1.7
Packagistghsa
web-token/jwt-framework
Affected versions
<3.4.10
Packagistghsa
web-token/jwt-framework
Affected versions
>=4.0.0 <4.0.7
Packagistghsa
web-token/jwt-framework
Affected versions
>=4.1.0 <4.1.7

Run on your own infrastructure? Check whether these packages are installed with threat-finder — our free open-source scanner.

AI-Powered Analysis

Machine-generated threat intelligence

AILast updated: 07/06/2026, 23:30:26 UTC

Technical Analysis

In the web-token/jwt-library, specifically in JWSVerifier.php line 144, the merging of protected and unprotected headers uses PHP's spread operator with the unprotected header spread last, allowing it to override the 'alg' parameter from the protected header. This leads to a TOCTOU vulnerability where HeaderCheckerManager validates the 'alg' from the protected header, but JWSVerifier uses the 'alg' from the unprotected header for verification. The same last-wins behavior occurs in JWEDecrypter.php lines 120-124 with array_merge for 'alg' and 'enc'. Attack vectors include using mixed key sets, placing 'alg' only in the unprotected header, or direct usage of JWSVerifier without HeaderCheckerManager. The recommended fix is to read 'alg' exclusively from the protected header, preventing unprotected headers from overriding it. A patch has been prepared for versions based on 3.4.x and planned for cascading merges into 4.0.x and 4.1.x.

Potential Impact

An attacker can exploit this vulnerability to perform algorithm confusion attacks by overriding the 'alg' parameter in the unprotected header, causing the system to verify the signature with a different algorithm than intended. This undermines the integrity guarantees of JWT tokens, potentially allowing unauthorized token acceptance or signature bypass. The vulnerability affects the security of JWT verification in affected versions of the web-token/jwt-library, leading to a high severity risk.

Mitigation Recommendations

A fix has been prepared that enforces reading the 'alg' parameter exclusively from the protected header, ensuring it cannot be overridden by unprotected headers. Users should upgrade to fixed versions once released. Until then, avoid using vulnerable versions and do not use JWSVerifier directly without HeaderCheckerManager. Patch status is not yet confirmed as fixed versions are pending release; check the vendor advisory for current remediation guidance.

Pro Console: star threats, build custom feeds, automate alerts via Slack, email & webhooks.Upgrade to Pro

Technical Details

Gcve Source
db.gcve.eu
Osv Id
GHSA-jc38-x7x8-2xc8
Osv Schema Version
1.4.0
Aliases
[]
Ecosystems
["Packagist"]
Database Specific Severity
HIGH
Cvss Version
4.0

Threat ID: 6a4c345527e9c797195fe24a

Added to database: 07/06/2026, 23:03:49 UTC

Last enriched: 07/06/2026, 23:30:26 UTC

Last updated: 07/31/2026, 12:27:30 UTC

Views: 20

Community Reviews

0 reviews

Crowdsource mitigation strategies, share intel context, and vote on the most helpful responses. Sign in to add your voice and help keep defenders ahead.

Sort by
Loading community insights…

Want to contribute mitigation steps or threat intel context? Sign in or create an account to join the community discussion.

Actions

PRO

Updates to AI analysis require Pro Console access. Upgrade inside Console → Billing.

Please log in to the Console to use AI analysis features.

Need more coverage?

Upgrade to Pro Console for AI refresh and higher limits.

For incident response and remediation, OffSeq services can help resolve threats faster.

Latest Threats

Breach by OffSeqOFFSEQFRIENDS — 25% OFF

Check if your credentials are on the dark web

Instant breach scanning across billions of leaked records. Free tier available.

Scan now
OffSeq TrainingCredly Certified

Lead Pen Test Professional

Technical5-day eLearningPECB Accredited
View courses