Commit 14c5317
committed
🔒 security: harden proxy middleware against SSRF, redirect downgrades, and hop-by-hop smuggling
Security audit of middleware/proxy and the redirect path in client/
identified ten findings. This change implements secure-by-default
behavior for all of them. See middleware/proxy/SECURITY_AUDIT.md for
the full report and severity table.
New API
- proxy.SecurityPolicy (AllowedSchemes, AllowPrivateIPs,
AllowHTTPSDowngrade, KeepHopByHopHeaders)
- proxy.DefaultSecurityPolicy, proxy.WithSecurityPolicy
- proxy.Config.SecurityPolicy, MaxResponseBodySize, MaxConnsPerHost
- Sentinel errors: ErrUpstreamSchemeNotAllowed, ErrUpstreamHostInvalid,
ErrUpstreamHostBlocked, ErrRedirectDowngrade
- client.ErrRedirectDowngrade
Breaking changes (defaults)
- Upstream targets resolving to loopback, RFC 1918, link-local
(including 169.254.169.254), multicast, unspecified, or RFC 6598
CGNAT addresses are rejected. Set AllowPrivateIPs to opt in.
- Only http/https schemes are accepted; file://, gopher://, ftp://,
etc. are rejected.
- DoRedirects rejects HTTPS→HTTP redirect downgrades.
- RFC 7230 §6.1 hop-by-hop headers (Connection, Keep-Alive,
Proxy-Authenticate, Proxy-Authorization, TE, Trailer,
Transfer-Encoding, Upgrade) are stripped both ways, plus every
header listed in the Connection field.
- TLSConfig is cloned with MinVersion: tls.VersionTLS12 when no
minimum is set; caller's struct is not mutated.
- Fiber HTTP client (client.composeRedirectURL) rejects HTTPS→HTTP
redirect downgrades.
Bug fixes
- DomainForward/BalancerForward previously concatenated `addr +
c.OriginalURL()`. A leading `//` in the request path could form a
network-path reference that, when re-parsed, changed the upstream
host. joinUpstreamPath now rebuilds the URL safely.
- Snapshot scheme/target into freshly allocated strings before
SetRequestURI to fix an aliasing regression where a caller-supplied
addr that was itself a slice of the request buffer got clobbered
mid-request (produced "unsupported protocol ttp:" errors).
Tests
- middleware/proxy/security_test.go: scheme allowlist, private-IP
blocking + opt-in, hop-by-hop stripping (request and response,
including Connection-listed), file:// rejection, HTTPS→HTTP
redirect blocking with a real TLS handshake, path-injection
protection, and TLS minimum version cloning.
- middleware/proxy/fuzz_test.go: FuzzValidateUpstream,
FuzzJoinUpstreamPath, FuzzConnectionListedHeaders (all pass with
-fuzztime=10s).
- TestMain in proxy_test.go relaxes the policy for loopback tests so
the rest of the suite continues to work; security_test.go installs
the strict policy where it asserts the new defaults.
- client/transport_test.go: TestComposeRedirectURL_RejectsHTTPSDowngrade.
Docs
- docs/middleware/proxy.md gains a full Security section covering
every default, the SecurityPolicy fields, and migration notes for
the breaking defaults.
https://claude.ai/code/session_01FhTTWhze2oLocDUWAkEet21 parent dec796e commit 14c5317
12 files changed
Lines changed: 1819 additions & 47 deletions
File tree
- client
- docs/middleware
- middleware/proxy
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
11 | 11 | | |
12 | 12 | | |
13 | 13 | | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
14 | 20 | | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
350 | 350 | | |
351 | 351 | | |
352 | 352 | | |
353 | | - | |
| 353 | + | |
| 354 | + | |
354 | 355 | | |
355 | 356 | | |
356 | 357 | | |
| |||
362 | 363 | | |
363 | 364 | | |
364 | 365 | | |
| 366 | + | |
365 | 367 | | |
366 | 368 | | |
367 | 369 | | |
368 | | - | |
| 370 | + | |
| 371 | + | |
369 | 372 | | |
370 | 373 | | |
371 | 374 | | |
372 | | - | |
| 375 | + | |
373 | 376 | | |
374 | 377 | | |
375 | 378 | | |
| 379 | + | |
| 380 | + | |
| 381 | + | |
| 382 | + | |
376 | 383 | | |
377 | 384 | | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
325 | 325 | | |
326 | 326 | | |
327 | 327 | | |
| 328 | + | |
| 329 | + | |
| 330 | + | |
| 331 | + | |
| 332 | + | |
| 333 | + | |
| 334 | + | |
| 335 | + | |
| 336 | + | |
| 337 | + | |
| 338 | + | |
| 339 | + | |
| 340 | + | |
| 341 | + | |
| 342 | + | |
| 343 | + | |
| 344 | + | |
| 345 | + | |
| 346 | + | |
| 347 | + | |
| 348 | + | |
| 349 | + | |
| 350 | + | |
| 351 | + | |
328 | 352 | | |
329 | 353 | | |
330 | 354 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
29 | 29 | | |
30 | 30 | | |
31 | 31 | | |
32 | | - | |
| 32 | + | |
33 | 33 | | |
34 | | - | |
| 34 | + | |
35 | 35 | | |
36 | | - | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
37 | 65 | | |
38 | 66 | | |
39 | 67 | | |
40 | 68 | | |
41 | 69 | | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
42 | 74 | | |
43 | 75 | | |
44 | 76 | | |
| |||
59 | 91 | | |
60 | 92 | | |
61 | 93 | | |
| 94 | + | |
| 95 | + | |
62 | 96 | | |
63 | 97 | | |
| 98 | + | |
64 | 99 | | |
65 | 100 | | |
66 | 101 | | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
67 | 112 | | |
68 | 113 | | |
69 | 114 | | |
| |||
181 | 226 | | |
182 | 227 | | |
183 | 228 | | |
184 | | - | |
185 | | - | |
| 229 | + | |
| 230 | + | |
186 | 231 | | |
187 | 232 | | |
| 233 | + | |
| 234 | + | |
188 | 235 | | |
189 | 236 | | |
190 | 237 | | |
| |||
198 | 245 | | |
199 | 246 | | |
200 | 247 | | |
| 248 | + | |
| 249 | + | |
| 250 | + | |
| 251 | + | |
| 252 | + | |
| 253 | + | |
| 254 | + | |
| 255 | + | |
| 256 | + | |
| 257 | + | |
| 258 | + | |
| 259 | + | |
| 260 | + | |
0 commit comments