fix(handler): move auth before request validation so probe resistance works (#875)
Probe resistance (probeResist=code:403) was always returning 400 Bad Request instead of the configured status code because the non-proxy-form request validation check (req.URL.Scheme != "http") ran before Authenticate(). Browser/scanner probes send GET / HTTP/1.1 which has an empty URL scheme, so they were rejected at the validation gate before probe resistance ever had a chance to respond. Swap the order: run Authenticate() first so probe resistance can intercept non-proxy-form probes with the configured decoy response. Legitimate clients with correct credentials proceed to request validation and routing as before.
This commit is contained in:
+17
-14
@@ -445,20 +445,9 @@ func (h *httpHandler) handleRequest(ctx context.Context, conn net.Conn, req *htt
|
|||||||
ro.HTTP.Response.Header = resp.Header
|
ro.HTTP.Response.Header = resp.Header
|
||||||
}()
|
}()
|
||||||
|
|
||||||
// HTTP/2 connection preface "PRI * HTTP/2.0" is rejected, as are
|
// Authenticate before request validation so that probe resistance
|
||||||
// non-CONNECT requests without an http:// scheme.
|
// can intercept non-proxy-form requests (e.g., browser/scanner probes
|
||||||
if req.Method == "PRI" ||
|
// that send "GET / HTTP/1.1" instead of proxy-form URLs).
|
||||||
(req.Method != http.MethodConnect && req.URL.Scheme != "http") {
|
|
||||||
resp.StatusCode = http.StatusBadRequest
|
|
||||||
|
|
||||||
if log.IsLevelEnabled(logger.TraceLevel) {
|
|
||||||
dump, _ := httputil.DumpResponse(resp, false)
|
|
||||||
log.Trace(string(dump))
|
|
||||||
}
|
|
||||||
|
|
||||||
return resp.Write(conn)
|
|
||||||
}
|
|
||||||
|
|
||||||
result := h.auth.Authenticate(ctx, req)
|
result := h.auth.Authenticate(ctx, req)
|
||||||
if !result.OK {
|
if !result.OK {
|
||||||
if result.PipeTo != "" {
|
if result.PipeTo != "" {
|
||||||
@@ -477,6 +466,20 @@ func (h *httpHandler) handleRequest(ctx context.Context, conn net.Conn, req *htt
|
|||||||
return errors.New("authentication failed")
|
return errors.New("authentication failed")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// HTTP/2 connection preface "PRI * HTTP/2.0" is rejected, as are
|
||||||
|
// non-CONNECT requests without an http:// scheme.
|
||||||
|
if req.Method == "PRI" ||
|
||||||
|
(req.Method != http.MethodConnect && req.URL.Scheme != "http") {
|
||||||
|
resp.StatusCode = http.StatusBadRequest
|
||||||
|
|
||||||
|
if log.IsLevelEnabled(logger.TraceLevel) {
|
||||||
|
dump, _ := httputil.DumpResponse(resp, false)
|
||||||
|
log.Trace(string(dump))
|
||||||
|
}
|
||||||
|
|
||||||
|
return resp.Write(conn)
|
||||||
|
}
|
||||||
|
|
||||||
log = log.WithFields(map[string]any{"clientID": result.ClientID})
|
log = log.WithFields(map[string]any{"clientID": result.ClientID})
|
||||||
ro.ClientID = result.ClientID
|
ro.ClientID = result.ClientID
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user