Goal
Add opt-in Encrypted Client Hello (ECH, draft-ietf-tls-esni-22) support to proxybroker's outbound TLS connections (judges, providers, proxy testing) so that on-path observers can't see the SNI hostnames being queried.
Why
When proxybroker connects to a TLS server (https://httpbin.org etc.), the server name appears in cleartext in the TLS handshake's SNI extension. Network operators (ISPs, employers, captive portals) routinely log or filter based on SNI — a 2024 measurement study found ~90% of HTTPS traffic has plaintext-SNI-based filtering deployed somewhere along its path.
For a tool whose sole purpose is privacy-related (proxies, anonymity), leaking the destination hostname in cleartext defeats the point. Users who deploy proxybroker on hostile networks (corporate wifi, repressive states, monitored mobile carriers) need the encrypted-DNS + encrypted-SNI combo to keep their proxy-testing activity off network logs.
State of ECH in 2026
- Cloudflare rolled out ECH at the edge in 2023 (limited rollout); reactivated 2024.
- Firefox 119+ ships ECH enabled by default (2024).
- Chrome 117+ ships ECH behind a flag.
- Python 3.12+ has tentative ECH support in the
ssl module via ECH_OUTER_SNI and friends (subject to platform OpenSSL ≥3.2).
- OpenSSL 3.2+ ships ECH support; 3.5+ has stable API.
- aiohttp doesn't expose ECH knobs directly but accepts a custom
SSLContext.
Scope
Resolver(ech=True) and Judge(ech=True) opt-in flags. Default False to preserve current behavior on systems without OpenSSL ECH support.
- Build the SSLContext with ECH enabled when the flag is set:
ctx = ssl.create_default_context()
if ech:
try:
ctx.set_ech_outer_sni("cloudflare-ech.com") # or per-judge
except (AttributeError, ssl.SSLError):
log.warning("ECH unavailable on this platform; falling back to plaintext SNI")
- Per-judge ECH config (some judges may not support ECH; let users opt out per host).
- Detection helper:
proxybroker.utils.has_ech_support() -> bool — returns True if Python+OpenSSL versions support ECH.
- Tests: mock SSLContext interaction; verify
ech=False is bit-identical to current behavior; verify ech=True raises useful error on platforms without support.
Acceptance criteria
Resolver(ech=True) constructs without error on supported platforms.
Resolver(ech=True) falls back gracefully (with warning) on unsupported platforms; doesn't crash startup.
has_ech_support() accurately reflects Python + OpenSSL capabilities at runtime.
- Documentation: a "Privacy mode" section in README explaining when to enable ECH and what guarantees it provides (and doesn't — it doesn't hide the destination IP, only the SNI).
- Existing tests pass; no behavior change with default
ech=False.
Out of scope
References
Estimated effort
~4-6 hours. Most of the work is figuring out the cross-platform SSL context setup and writing the docs that explain what ECH does and doesn't protect against.
Goal
Add opt-in Encrypted Client Hello (ECH, draft-ietf-tls-esni-22) support to proxybroker's outbound TLS connections (judges, providers, proxy testing) so that on-path observers can't see the SNI hostnames being queried.
Why
When proxybroker connects to a TLS server (
https://httpbin.orgetc.), the server name appears in cleartext in the TLS handshake's SNI extension. Network operators (ISPs, employers, captive portals) routinely log or filter based on SNI — a 2024 measurement study found ~90% of HTTPS traffic has plaintext-SNI-based filtering deployed somewhere along its path.For a tool whose sole purpose is privacy-related (proxies, anonymity), leaking the destination hostname in cleartext defeats the point. Users who deploy proxybroker on hostile networks (corporate wifi, repressive states, monitored mobile carriers) need the encrypted-DNS + encrypted-SNI combo to keep their proxy-testing activity off network logs.
State of ECH in 2026
sslmodule viaECH_OUTER_SNIand friends (subject to platform OpenSSL ≥3.2).SSLContext.Scope
Resolver(ech=True)andJudge(ech=True)opt-in flags. DefaultFalseto preserve current behavior on systems without OpenSSL ECH support.proxybroker.utils.has_ech_support() -> bool— returns True if Python+OpenSSL versions support ECH.ech=Falseis bit-identical to current behavior; verifyech=Trueraises useful error on platforms without support.Acceptance criteria
Resolver(ech=True)constructs without error on supported platforms.Resolver(ech=True)falls back gracefully (with warning) on unsupported platforms; doesn't crash startup.has_ech_support()accurately reflects Python + OpenSSL capabilities at runtime.ech=False.Out of scope
servemode — proxybroker's serve mode is HTTP CONNECT, doesn't terminate TLS.Resolveritself — separate ticket (see SOTA polish: connection-layer Happy Eyeballs + DNSSEC + IDN (post-#212) #213 for DNS privacy items).References
sslmodule ECH APIEstimated effort
~4-6 hours. Most of the work is figuring out the cross-platform SSL context setup and writing the docs that explain what ECH does and doesn't protect against.