Repository navigation
httpjet v0.0.4
httpjet 0.0.4 — security release
This release fixes the findings of a whole-codebase security review (2026-10-06) and moves every dependency and the toolchain to their latest versions. Upgrading is recommended for anyone serving HTTP/3.
HTTP/3 (high)
- Connection-budget exhaustion before address validation. A new QUIC connection used to take a permit from the connection budget shared with TCP before the client's address was validated, so spoofed Initials could starve TCP accepts. The budget is now charged when the handshake completes. Once 64 handshakes are pending on a core, Initials from unvalidated addresses get a stateless Retry. Handshakes are capped at 256 per core and time out after 10 s.
- quinn-proto 0.11.19 (GHSA-53rm-773f-4q8c: one MAX_STREAM_DATA frame could open 2^40 streams). The request-stream accept loop also enforces its own bound, independent of quinn: at most 256 new request streams per connection drive and 512 tracked requests per connection, otherwise the connection closes with
H3_EXCESSIVE_LOAD. The same quinn release fixes a client-side Retry issue (GHSA-wppq-2f6r-wfvm) that does not affect httpjet's server role. Thanks to the quinn maintainers for the fixes.
Request handling
- Rewrite targets are decoded once. An internal rewrite target is percent-decoded and normalized once, matching Apache's internal-redirect semantics. Previously, destination-scoped policy (
Headerdirectives, body limits, static contexts) judged the still-encoded path while the static handler decoded and served it. - Client headers reaching PHP/CGI follow one rule. This applies to the LSAPI env table, the LSAPI header block and FastCGI params. Underscore-named headers (which lsphp mapped to
HTTP_*through the header block),Proxy, and forwarding-authority headers (CF-Connecting-IP,X-Forwarded-*,X-Real-IP,True-Client-IP,Forwarded) from untrusted peers no longer reach the application. - HTTP/1.1 request targets and Host. RFC 9112 target forms and a valid
Hostare required. An absolute-form authority replacesHost.CONNECTand heads with bare-LF line endings are refused before dispatch. - Basic auth.
AuthUserFilemust open inside the docroot, checked on the opened descriptor. Use--auth-user-file-rootsfor other locations.- Unknown users pay the same bcrypt cost as known ones.
- bcrypt cost is capped at 14.
- Plaintext htpasswd entries no longer verify, as in Apache on Unix.
- An unusable user file returns 500.
Control surfaces
/__hj_cache_purgeaccepts onlyPOST(405 +Allowotherwise)./__hj_cache_readyaccepts onlyGET/HEAD.checkwarns that unix-socket listeners expose both.- The page-cache integrity key is opened through a validated path.
httpjet_pagecache_integrity_activereports its state, and--page-cache-integrity-requiredrefuses to run the file tier without it. - SIGHUP reloads time out instead of waiting indefinitely. Config files are size-capped. Changes to
proxyProtocol,proxyProtocolPeersor a unix-socket path are classified as restart-required. - New
--profile-token-file. The metrics listener is capped at 64 concurrent connections. The lsphp control socket is created through a rename, so it never exists with looser permissions.
Dependencies and tooling
- Dependencies are refreshed to their latest releases. The vendored forks moved to socket2 0.6, mio 1 and thiserror 2. The unmaintained rustls-pemfile and fxhash were replaced.
cargo audit --deny warningsis clean. - Rust 1.99.0 is now the minimum and CI toolchain.
- New daily
Dependency advisoriesworkflow. It also watches transport-critical crates for releases that advisory databases have not caught up with yet. - New clippy panic-site ratchet for the request-serving crates, since release builds use
panic = "abort". - New fuzz targets:
qpack_decode,proxy_protocol_parse,lscache_control.
Upgrade notes
- HTTP/1.1 clients must send a valid
Host. Absolute-form requests have their authority used asHost. - Operators keeping htpasswd files outside the docroot need
--auth-user-file-roots. Plaintext htpasswd entries must be re-hashed with bcrypt (htpasswd -B). - Local purge scripts must use
POST.