fix(core): bound the compact serializers' segment split - #675
Merged
Conversation
Spomky
force-pushed
the
hardening/compact-serializer-explode-limit
branch
from
August 26, 2026 09:02
3da88fc to
c7ea9ec
Compare
Spomky
force-pushed
the
fix/qa-tooling-4-1-x
branch
from
August 26, 2026 09:06
e96c16c to
94de666
Compare
Spomky
force-pushed
the
hardening/compact-serializer-explode-limit
branch
from
August 26, 2026 09:07
c7ea9ec to
c94783d
Compare
Spomky
force-pushed
the
fix/qa-tooling-4-1-x
branch
from
August 26, 2026 09:14
94de666 to
9a97e9b
Compare
Spomky
force-pushed
the
hardening/compact-serializer-explode-limit
branch
4 times, most recently
from
August 26, 2026 09:40
e4f64a4 to
956437c
Compare
The compact JWS and JWE serializers split the whole input on every "." before checking the segment count, so a delimiter-heavy string was first expanded into one array entry per delimiter. That costs about 25 times the size of the input in memory: a 2 MB token allocates ~48 MB before it is rejected as malformed. The split is now bounded to one more segment than a valid token has, which is enough to detect and reject longer input while keeping the allocation proportional to the token itself. The accepted and rejected inputs are unchanged. Reported by Team Atlanta.
Spomky
force-pushed
the
hardening/compact-serializer-explode-limit
branch
from
August 26, 2026 09:47
956437c to
0e4dfbf
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
The compact JWS and JWE serializers split the whole input on every
.before checking the segment count, so a delimiter-heavy string was first expanded into one array entry per delimiter.The split is now bounded to one segment more than a valid token has —
4for JWS,6for JWE. That extra segment is enough to detect and reject any longer input, so the accepted and rejected inputs are unchanged:count($parts) !== 3(resp.!== 5) still rejects everything it rejected before.Why
Reported by Team Atlanta as a memory-exhaustion issue on
JWSLoader::loadAndVerifyWithKeySet().This is hardening rather than a vulnerability, so it is being fixed in the open with no advisory. The measured amplification is about 25×, which is the ordinary overhead of a PHP packed array and nothing more:
explodewithout limit.Reaching a real memory limit therefore requires an application to accept multi-megabyte bearer tokens with no length cap of its own. Worth fixing because the fix is free, not because the exposure is meaningful.
The report only covered JWS;
Encryption\Serializer\CompactSerializerhad the same shape and is fixed alongside it.Tests
tests/Component/{Signature,Encryption}/CompactSerializerTest.phpcover the rejection of an over-long token and assert the allocation stays bounded. Both regression tests fail without the fix (33.5 MB allocated) and pass with it.Note for anyone touching them: the bound has to be measured with
memory_reset_peak_usage()+memory_get_peak_usage(). A plainmemory_get_usage()delta reads as zero, because the array is freed as the exception unwinds.Notes on the branch state
Unrelated to this change, but visible when validating it on
4.1.x:ComposerJsonTestdiverging on thebrick/mathconstraint). This PR adds none — checked against a stashed baseline.4.1.x:Undefined constant SetList::PHPUNIT, hidden behind a crash in its own error printer. That was repaired on4.2.xin Make the quality gates pass again #669 and never backported.Reported by Team Atlanta.