Files
tech-log-backend/docs/httpclient/security.md
T

3.6 KiB

HTTP Client Platform — Security Guide

What the platform owns

Authorization, Proxy-Authorization, Host, Content-Length, Transfer-Encoding, Traceparent, Tracestate, Baggage, and (unless a profile opts in) Cookie are platform-owned. A caller cannot set them. Idempotency-Key is accepted only when the operation declares it. Any header name or value containing CR or LF is rejected before the request is built.

Target policy

A trusted profile accepts only a profile-relative URI template. An absolute URI is rejected rather than sanitised: varying the destination is what H3 is for, and H3 has its own policy, credentials, and address validation. Template variables are encoded per component, so a value containing /, ?, or # cannot change the shape of the request.

TLS

Allowed: TLS 1.2 and 1.3, hostname verification, the JVM trust store, a per-profile custom CA, a per-profile client certificate, mTLS, SNI and ALPN, and certificate rotation through a new runtime generation.

Forbidden and unrepresentable: a trust-all trust manager, disabled hostname verification, ignoring certificate errors, automatically trusting a production self-signed certificate, falling back to plaintext after an HTTPS failure, and writing key material into configuration or logs.

Unknown CA, hostname mismatch, expired certificate, revoked certificate, protocol mismatch, and a missing client certificate are permanent. Only a transient handshake timeout may be retried, inside the deadline.

Dynamic Target (SSRF)

Every hop — the first one included — runs the whole flow:

  1. strict URI parse
  2. scheme allowlist
  3. reject userinfo and invalid ports
  4. IDNA-canonicalise the host
  5. host allowlist or suffix policy
  6. resolve every A and AAAA answer
  7. normalise each address, including IPv4-mapped IPv6
  8. reject loopback, link-local, RFC1918, ULA, carrier-grade NAT, unspecified, multicast, cloud metadata, and organisation-defined ranges
  9. pin the connection to the approved addresses through the same validated resolver
  10. apply response size and content policy
  11. repeat for each redirect

Any forbidden address in the answer set rejects the whole target. Validating only the first answer would let a host that resolves to one public and one private address through.

Dynamic profiles inherit no API key, OAuth token, Cookie, or default header, and no Cookie jar is created. A specific host may be granted a credential only through an explicitly registered DynamicCredentialBinding.

Application-level validation is not sufficient on its own. A network control — Kubernetes NetworkPolicy, service-mesh egress policy, firewall, or proxy ACL — is an operational completion requirement.

Redirects

Disabled by default. Engine redirect handling is off in every transport so the platform can re-validate each hop. 307 and 308 preserve method and body and are therefore allowed only for a replayable body. Cross-origin hops are refused unless the profile opts in, and when they are allowed Authorization, Proxy-Authorization, Cookie, and API-key headers are stripped.

Observability

Allowed tags: clientName, operationName, method, uriTemplate, status, outcome, transport, protocol, timeoutType, retryReason, evidence, circuitState.

Rejected outright: full URL, query parameters, path variable values, user ID, raw tenant ID, resolved IP, API key, token, Cookie, idempotency key, request or response body, exception message.

Failures are logged once, structured, at the end of a logical call. Retry attempts are DEBUG or span events. URLs appear only as templates.