Skip to content

Commit 081cb9e

Browse files
committed
security(proxy): isolate authenticated pool keys
Assign an opaque identity to each authenticated proxy configuration and include it in the connection pool key. This prevents SOCKS5 and HTTP CONNECT connections from being reused across configurations without exposing credentials in internal keys.
1 parent 626dac7 commit 081cb9e

2 files changed

Lines changed: 22 additions & 1 deletion

File tree

src/http_proxy.jl

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
using EnumX
44
using Reseau.HostResolvers
5+
using UUIDs
56

67
struct _NoProxyIPRule{N}
78
ip::NTuple{N,UInt8}
@@ -45,6 +46,7 @@ struct _ProxyTarget
4546
authorization::Union{Nothing,String}
4647
username::Union{Nothing,String}
4748
password::Union{Nothing,String}
49+
pool_identity::Union{Nothing,UUID}
4850
end
4951

5052
"""
@@ -354,6 +356,7 @@ function _parse_socks_proxy_target(value::AbstractString, scheme::String)::_Prox
354356
authorization,
355357
username,
356358
password,
359+
authorization === nothing ? nothing : uuid4(),
357360
)
358361
end
359362

@@ -375,6 +378,7 @@ function _parse_proxy_target(url::AbstractString, allow_unsupported::Bool=false)
375378
parsed.authorization,
376379
nothing,
377380
nothing,
381+
parsed.authorization === nothing ? nothing : uuid4(),
378382
)
379383
end
380384

@@ -498,5 +502,8 @@ function _proxy_plan(
498502
else
499503
secure ? _ProxyPlanMode.HTTP_TUNNEL : _ProxyPlanMode.HTTP_FORWARD
500504
end
501-
return _ProxyPlan(mode, proxy::_ProxyTarget, (proxy::_ProxyTarget).address, string((proxy::_ProxyTarget).url, "|", secure ? "https://" : "http://", address))
505+
proxy_target = proxy::_ProxyTarget
506+
proxy_pool_key = proxy_target.pool_identity === nothing ?
507+
proxy_target.url : string(proxy_target.url, "|auth=", proxy_target.pool_identity::UUID)
508+
return _ProxyPlan(mode, proxy_target, proxy_target.address, string(proxy_pool_key, "|", secure ? "https://" : "http://", address))
502509
end

test/http_client_proxy_tests.jl

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -577,6 +577,20 @@ end
577577
@test socks5h_plan.first_hop_address == "proxy.local:1080"
578578
@test socks5h_plan.pool_key == "socks5h://proxy.local:1080/|https://origin.local:443"
579579

580+
socks_alice = HT.ProxyURL("socks5://alice:one@proxy.local:1080")
581+
socks_bob = HT.ProxyURL("socks5://bob:two@proxy.local:1080")
582+
socks_alice_plan = HT._proxy_plan(socks_alice, false, "origin.local:80")
583+
socks_bob_plan = HT._proxy_plan(socks_bob, false, "origin.local:80")
584+
@test socks_alice_plan.pool_key != socks_bob_plan.pool_key
585+
@test socks_alice_plan.pool_key == HT._proxy_plan(socks_alice, false, "origin.local:80").pool_key
586+
@test !occursin("alice", socks_alice_plan.pool_key)
587+
@test !occursin("one", socks_alice_plan.pool_key)
588+
589+
tunnel_alice = HT.ProxyURL("http://alice:one@proxy.local:8080")
590+
tunnel_bob = HT.ProxyURL("http://bob:two@proxy.local:8080")
591+
@test HT._proxy_plan(tunnel_alice, true, "origin.local:443").pool_key !=
592+
HT._proxy_plan(tunnel_bob, true, "origin.local:443").pool_key
593+
580594
socks_no_proxy = HT.ProxyURL("socks5://proxy.local:1080"; no_proxy = "origin.local")
581595
skipped_plan = HT._proxy_plan(socks_no_proxy, false, "origin.local:80")
582596
@test skipped_plan.mode == HT._ProxyPlanMode.DIRECT

0 commit comments

Comments
 (0)