Summary
joserfc 1.7.4 defines compact-JWS header, payload, and signature limits, but
calls value.split(b".") before validating them. Python bytes.split() copies
the attacker-controlled segments, so the current guard allocates roughly one
complete payload before rejecting it.
Reproduction
.venv-research/bin/python 06-practical-testing/preauth-memory-profile.py
The input is resident before a fork-isolated verifier runs, and every signature
is invalid.
| Input |
Peak RSS delta |
Amplification |
Result |
| 1 MiB |
1.11 MiB |
1.11× |
size rejected |
| 4 MiB |
4.04 MiB |
1.01× |
size rejected |
| 8 MiB |
8.04 MiB |
1.01× |
size rejected |
| 16 MiB |
16.04 MiB |
1.00× |
size rejected |
| 32 MiB |
31.74 MiB |
0.99× |
size rejected |
| 64 MiB |
63.80 MiB |
1.00× |
size rejected |
The unconstrained profile is the primary evidence: joserfc allocates an
additional attacker-sized object before producing its intended
ExceededSizeError. A separate baseline-plus-headroom stress test raises
MemoryError when that copy cannot fit, but this report does not characterize
an absolute RLIMIT_AS value as a realistic worker memory budget.
Security Argument
The issue is not that joserfc can prevent the transport layer from receiving a
large header. The issue is narrower: once bytes reach deserialize_compact(),
the library's existing size guard cannot fulfill its allocation-limiting purpose
because attacker-controlled segment copies are created first.
This differs from
GHSA-frfh-8v73-gjg4,
which concerned embedding already-loaded oversized segments in exception and log
messages. Here, the current implementation itself creates an additional
payload-sized allocation before its fixed size exception.
Recommended Fix
Check len(value) against a maximum compact-serialization size before
split(). If per-segment diagnostics are needed, locate delimiters with bounded
find() operations or memory views, validate offsets and lengths, and only then
materialize acceptable segments.
References
Summary
joserfc 1.7.4 defines compact-JWS header, payload, and signature limits, but
calls
value.split(b".")before validating them. Pythonbytes.split()copiesthe attacker-controlled segments, so the current guard allocates roughly one
complete payload before rejecting it.
Reproduction
The input is resident before a fork-isolated verifier runs, and every signature
is invalid.
The unconstrained profile is the primary evidence: joserfc allocates an
additional attacker-sized object before producing its intended
ExceededSizeError. A separate baseline-plus-headroom stress test raisesMemoryErrorwhen that copy cannot fit, but this report does not characterizean absolute
RLIMIT_ASvalue as a realistic worker memory budget.Security Argument
The issue is not that joserfc can prevent the transport layer from receiving a
large header. The issue is narrower: once bytes reach
deserialize_compact(),the library's existing size guard cannot fulfill its allocation-limiting purpose
because attacker-controlled segment copies are created first.
This differs from
GHSA-frfh-8v73-gjg4,
which concerned embedding already-loaded oversized segments in exception and log
messages. Here, the current implementation itself creates an additional
payload-sized allocation before its fixed size exception.
Recommended Fix
Check
len(value)against a maximum compact-serialization size beforesplit(). If per-segment diagnostics are needed, locate delimiters with boundedfind()operations or memory views, validate offsets and lengths, and only thenmaterialize acceptable segments.
References