chore: record pre-existing uncommitted repository state
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>
This commit is contained in:
co-authored by
Claude Opus 5
parent
1a3b560678
commit
5f10b791d3
@@ -0,0 +1,75 @@
|
||||
# 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.
|
||||
Reference in New Issue
Block a user