Skip to content

httpjet v0.0.4

Choose a tag to compare

@faratech faratech released this 07 Oct 03:03
· 1 commit to main since this release

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 (Header directives, 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 Host are required. An absolute-form authority replaces Host. CONNECT and heads with bare-LF line endings are refused before dispatch.
  • Basic auth.
    • AuthUserFile must open inside the docroot, checked on the opened descriptor. Use --auth-user-file-roots for 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_purge accepts only POST (405 + Allow otherwise). /__hj_cache_ready accepts only GET/HEAD. check warns that unix-socket listeners expose both.
  • The page-cache integrity key is opened through a validated path. httpjet_pagecache_integrity_active reports its state, and --page-cache-integrity-required refuses to run the file tier without it.
  • SIGHUP reloads time out instead of waiting indefinitely. Config files are size-capped. Changes to proxyProtocol, proxyProtocolPeers or 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 warnings is clean.
  • Rust 1.99.0 is now the minimum and CI toolchain.
  • New daily Dependency advisories workflow. 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 as Host.
  • 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.