Snapshot of the in-flight state that already existed, identically, in both this worktree and the main checkout before this session began: the initial HTTP Client platform implementation (previously untracked), the redis-lab removal, and the JPA / object-storage / notification integration work. Kept separate from this session's HTTP Client review response, which lands in the following commit, so the two bodies of work stay reviewable apart. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
76 lines
3.6 KiB
Markdown
76 lines
3.6 KiB
Markdown
# 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.
|