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

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.