Memory-Safe Stack Eliminates a CVE Class
Most of the QUIC CVEs of 2024-2025 are implementation bugs, not protocol flaws. CVE-2024-24989 and CVE-2024-35200 were memory-safety defects in NGINX’s C QUIC module. CVE-2025-54939 (“QUIC-LEAK”) is a pre-handshake memory exhaustion in LiteSpeed’s LSQUIC. CVE-2025-4820 is an optimistic-ACK congestion-control flaw in Cloudflare’s quiche. A proxy does not “mitigate” these by configuration - it avoids the entire bug class by not running the affected code.
pqcrypta-proxy speaks HTTP/3, QUIC, and WebTransport through the Rust
quinn 0.11 + h3 + rustls stack
(wtransport for WebTransport), all memory-safe. The endpoint advertises
QUIC v1 only (RFC 9000) plus the reserved GREASE version per
RFC 9287, overriding quinn’s default list so Version Negotiation never offers
the obsolete draft versions it cannot actually complete. That is a deliberate
downgrade-surface reduction at the endpoint layer.
The Vulnerability โ Mitigation Map
Each row is a documented issue, tagged with one or more of the Implemented Stack-avoided Bounded classes defined in “How to read the mapping” above.
| Documented issue | Class | pqcrypta-proxy control |
|---|---|---|
| 0-RTT / early-data replay RFC 9001 ยง9.2, TLS 1.3 early data |
Implemented | 0-RTT replay nonce store (strict/session/none); per-route enable_0rtt; startup conflict check rejects 0-RTT on non-safe routes - tls_acceptor.rs |
| UDP amplification / reflection RFC 9000 ยง8.1; QUIC-LEAK CVE-2025-54939 |
Stack-avoided | quinn enforces the 3ร anti-amplification limit + stateless Retry address validation; LSQUIC bug not in this stack - quic_listener.rs |
| Optimistic / acknowledgement-based ACK DDoS CVE-2025-4820 (quiche) |
Stack-avoided Bounded | Congestion control is quinn’s, not quiche’s; per-IP / JA3 rate limiting + circuit breaker cap the rate any single peer can extract |
| QUIC handshake flooding QFAM research (arXiv 2412.08936) |
Stack-avoided Bounded | Retry-based address validation before crypto state is allocated; multi-dimensional rate limiting on the QUIC path; connection caps |
| Rapid Reset stream-flood DoS CVE-2023-44487 (H2) + H3 stream variant |
Implemented Bounded | Rate limiting applied on both TCP (H1/H2) and QUIC/HTTP3 paths; tested by pentest Script 07; circuit breaker isolates a stressed backend |
| NGINX HTTP/3 memory defects CVE-2024-24989 / CVE-2024-35200 |
Stack-avoided | Memory-safe Rust h3/quinn; not the NGINX C QUIC module |
| Cross-origin WebTransport hijacking same-origin policy (WebTransport overview draft) |
Implemented | SR-02 origin validation: webtransport_allowed_origins allowlist, 403 on mismatch - webtransport handler |
| WebTransport stream / datagram flooding multiplexed-channel resource exhaustion |
Implemented | Per-origin limits: max sessions/origin, streams/session, datagrams/sec enforced |
| PQC downgrade attack classical-only forced negotiation |
Implemented | PQC downgrade detection: block (421) / log / allow when X25519MLKEM768 was required - pqc_tls.rs |
| TCP middlebox / WAF blindness on QUIC encrypted transport header |
Implemented | WAF + JA3/JA4 fingerprinting + rate limiting run at the proxy on the decrypted H3/WebTransport path, not on a blind appliance |
| MASQUE / CONNECT-UDP open-relay abuse RFC 9298 tunnelling |
Implemented | Disabled by default; host:port allowlist, per-session idle timeout, per-connection session cap - [masque] |
| Transfer-Encoding injection / H3 smuggling RFC 9114 ยง4.2 |
Implemented | Hop-by-hop headers stripped before forwarding; TE prohibited on H3; tested across 5 protocols - see HTTP Smuggling research |
IP spoofing via forwarding headersX-Forwarded-For / True-Client-IP trust |
Implemented | XFF never used for access control; SSRF patterns exempt legitimate proxy-hop loopback/RFC1918 in XFF to avoid false positives |
| Connection-ID linkability / migration tracking RFC 9000 privacy considerations |
Bounded | Migration is operator-controllable via enable_quic_migration; quinn issues multiple connection IDs per RFC 9000 |
0-RTT Replay (RFC 9001 ยง9.2)
TLS 1.3 0-RTT (and QUIC’s early data) lets a client send application data in the first flight, before the handshake completes. RFC 9001 ยง9.2 is explicit that this data is replayable: a network attacker can capture the early-data packet and re-send it. The consequence is that a non-idempotent request - place an order, change a password, transfer funds - can be replayed for duplicate effect, and the usual server-side defences break down in distributed / CDN deployments where the anti-replay state is not shared.
pqcrypta-proxy treats 0-RTT as opt-in and replay-guarded by construction:
# Per-route control - deny early data on anything non-idempotent
[[routes]]
path = "/api/"
enable_0rtt = false # route never accepts 0-RTT
# Global early-data replay guard
nonce_store_mode = "strict" # strict | session | none
# strict rejects any replayed early-data nonce
# inside the configurable window
Two structural backstops sit behind the config. The nonce store in
tls_acceptor.rs rejects a replayed early-data nonce within the window. And
the startup config-conflict validator refuses to boot a configuration
that enables 0-RTT on a non-safe route without replay protection, so the unsafe
combination cannot be deployed. The protocol flaw is not removed, but every path that
could exploit it is closed or guarded, and an unsafe config fails closed at startup.
Denial of Service: Amplification, Handshake Flood, Optimistic ACK
QUIC moved the cheapest DoS pressure to the connection setup and congestion-control machinery. Three documented variants, three different answers:
Amplification / reflection (RFC 9000 ยง8.1)
An off-path attacker spoofs a victim’s address in an Initial packet hoping the server floods the victim with handshake data. The RFC mandates a server send no more than three times the bytes received from an unvalidated address, and validate the address with a Retry token before exceeding it. quinn enforces both. QUIC-LEAK (CVE-2025-54939) showed LSQUIC mishandling this and leaking memory pre-handshake - a bug in that implementation, not in the quinn stack pqcrypta-proxy runs.
Handshake flooding (QFAM, arXiv 2412.08936)
Researchers showed attackers can drive CPU and memory cost by abusing address validation and version negotiation to force the server into repeated crypto work. The structural defence is to validate the source address with a stateless Retry before allocating crypto state, which quinn does, and to cap connection establishment rate per source. pqcrypta-proxy layers multi-dimensional rate limiting (IP, JA3/JA4, composite keys) onto the QUIC path and applies connection caps, so a flood is throttled at the edge rather than converted into unbounded handshake work.
Optimistic ACK (CVE-2025-4820)
A peer acknowledges data it never received to trick the sender into inflating its congestion window and over-sending, an amplification and CPU-burn vector. The flaw was in Cloudflare quiche’s congestion controller; the fix was a CWND-aware ACK-skip. pqcrypta-proxy’s congestion control is quinn’s, so the specific defect does not apply, and the effect any single peer can produce is bounded by per-IP rate limiting and the circuit breaker. This is a bounded row, not a custom fix.
WebTransport Attack Surface
WebTransport runs over HTTP/3 on a dedicated UDP port (4433 by default,
webtransport_port configurable). Two properties raise its risk. First, it
follows the same-origin policy, but the server must enforce it: the IETF
WebTransport overview draft requires the server to validate the Origin of browser-initiated
sessions. Second, a TCP probe of the UDP port returns connection-refused, so scanners that
only test TCP report no service and skip the surface.
pqcrypta-proxy enforces origin at the session boundary (SR-02): a browser session
whose Origin is not in webtransport_allowed_origins is rejected
with 403; non-browser clients that send no Origin are handled under a separate rule rather
than blanket-trusted. Resource exhaustion across the multiplexed channel is capped by
per-origin limits - maximum sessions per origin, streams per session, and
datagrams per second - so a single origin cannot exhaust the listener by opening
unbounded streams or flooding datagrams.
[server] webtransport_port = 4433 webtransport_allowed_origins = ["https://pqcrypta.com"] # SR-02 allowlist # per-origin caps: max sessions/origin, streams/session, datagrams/sec
Inspecting HTTP/3 Requires Terminating QUIC
Traditional IDS / IPS / DPI / inline-WAF appliances inspect reassembled TCP byte streams. QUIC encrypts the transport header, so to a network device the traffic is opaque UDP: it sees ports and nothing else. Signatures those devices carry for HTTP attacks do not fire on QUIC. Devices that cannot inspect QUIC often block UDP/443, forcing clients to fall back to HTTP/2 over TCP, which moves the attack surface to the TCP path rather than removing it.
The only place left to inspect HTTP/3 and WebTransport is the endpoint that holds the session keys. pqcrypta-proxy terminates QUIC and runs its full inspection stack on the decrypted requests: the pattern-based WAF (SQLi, XSS, traversal, NoSQLi, SSRF, command injection, XXE, deserialization - OWASP A01/A03/A08/A10), JA3/JA4 fingerprinting with replay and drift detection, and multi-dimensional rate limiting - all applied identically on TCP (H1/H2) and QUIC/HTTP3 paths. The inspection point is the proxy, not a middlebox that QUIC made blind.
Verifying the Mitigations
The mitigations above are verified on every deploy, not assumed. The pqcrypta-proxy pentest suite runs 32 automated black-box attack scripts across 12 phases on every deploy, covering WAF bypass, HTTP smuggling across HTTP/1.1, HTTP/2, HTTP/3 and WebTransport, SSRF, timing oracles, race conditions, and the QUIC/WebTransport port-isolation checks. To reproduce the core QUIC-layer checks yourself:
- Confirm the endpoint advertises only
h3via Alt-Svc and only QUIC v1 in Version Negotiation. - TCP-probe the WebTransport port (4433) - expect connection refused; a TCP listener there is a misconfiguration.
- Open a WebTransport session from an origin not on the allowlist - expect 403.
- Force a classical-only TLS ClientHello where PQC is required - expect a 421 (or an audit-log downgrade event, per policy).
- Send early data on a route with
enable_0rtt = false- expect rejection; replay a captured early-data packet - expect the nonce store to drop it.
For the full transport-layer architecture and the QUIC-vs-TCP performance case, see the QUIC vs TCP Real-World Analysis whitepaper and the pqcrypta-proxy documentation.
Frequently Asked Questions
strict/session/none)
rejects replayed early-data nonces within a configurable window; per-route
enable_0rtt lets non-idempotent routes deny early data outright; and
a startup config-conflict check refuses to run 0-RTT on non-safe routes without
replay protection.quinn + h3 + rustls
stack, which was not the affected codebase. Anti-amplification and stateless
Retry are enforced by quinn; rate limiting and the circuit breaker bound any
residual congestion-state or handshake abuse.webtransport_allowed_origins with a
403, and per-origin limits cap sessions, streams, and datagram rate.