From 796eff444a18f6ca20cbf56e34368432d1b0ee5e Mon Sep 17 00:00:00 2001 From: Vladimir Dubrovin <3proxy@3proxy.ru> Date: Fri, 13 Nov 2026 17:14:36 +0300 Subject: [PATCH] httpsrv: credentials of an earlier request not inherited by the next --- src/httpsrv.c | 8 ++++++++ tests/cases/httpsrv_auth.py | 18 ++++++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/src/httpsrv.c b/src/httpsrv.c index 7bafb69..6562874 100644 --- a/src/httpsrv.c +++ b/src/httpsrv.c @@ -1585,6 +1585,14 @@ void * httpsrvchild(struct clientparam *param) r.lasthost = lasthost; param->res = 0; + /* What the request brought belongs to it alone. A connection + can carry a client's request after another's, as a reverse + proxy reuses its upstreams, so the credentials and the host + of an earlier request decide nothing for this one. */ + *param->hostname = 0; + *param->username = 0; + *param->password = 0; + answered = httpsrv_request(param, &r); handoff = r.handoff; param->climark = -1; diff --git a/tests/cases/httpsrv_auth.py b/tests/cases/httpsrv_auth.py index c0b971e..7cd3290 100644 --- a/tests/cases/httpsrv_auth.py +++ b/tests/cases/httpsrv_auth.py @@ -1,5 +1,7 @@ """Authentication and access rules in front of the HTTP server.""" +import base64 + def run(t): if not t.skip_unless("httpsrv"): @@ -49,3 +51,19 @@ def run(t): t.contains(t.http(url + "/echo", auth=("alice", "secret")), "path=/echo", "an authenticated request is dispatched") + + # --- credentials do not outlive the request that carried them ------ + # A kept connection answers a second request without credentials as + # unauthenticated: a reverse proxy reuses its upstreams between + # clients, so one client's login must not open another's way in. + creds = base64.b64encode(b"alice:secret").decode() + text, closed = t.raw_session(srv, + f"GET /echo HTTP/1.1\r\nHost: t\r\n" + f"Authorization: Basic {creds}\r\n\r\n" + f"GET /echo HTTP/1.1\r\nHost: t\r\nConnection: close\r\n\r\n") + parts = text.split("HTTP/1.") + t.eq(3, len(parts), "the kept connection answers both requests") + t.eq(True, parts[1].startswith("1 200"), + "the request with credentials is allowed") + t.eq(True, parts[2].startswith("0 401"), + "the request after it, without credentials, is refused")