diff --git a/custom-server.js b/custom-server.js index a12aa747..764a2df6 100644 --- a/custom-server.js +++ b/custom-server.js @@ -11,9 +11,14 @@ http.createServer = (...args) => { if (!handler) return origCreate(...args); const wrapped = (req, res) => { const ip = req.socket && req.socket.remoteAddress ? req.socket.remoteAddress : ""; + // Forwarding headers present = request arrived via a reverse proxy; loopback + // socket is the proxy hop, not the end-user, so it must not be trusted as local. + const viaProxy = !!(req.headers["x-forwarded-for"] || req.headers["x-real-ip"]); delete req.headers["x-9r-real-ip"]; delete req.headers["x-forwarded-for"]; + delete req.headers["x-9r-via-proxy"]; req.headers["x-9r-real-ip"] = ip; + if (viaProxy) req.headers["x-9r-via-proxy"] = "1"; return handler(req, res); }; return origCreate(...rest, wrapped); diff --git a/src/dashboardGuard.js b/src/dashboardGuard.js index 47ced963..5a7a2588 100644 --- a/src/dashboardGuard.js +++ b/src/dashboardGuard.js @@ -90,6 +90,9 @@ function isLoopbackHostname(h) { } export function isLocalRequest(request) { + // Stamped by custom-server.js when forwarding headers exist: request came through + // a reverse proxy, so the loopback socket is the proxy hop, not the end-user. + if (request.headers.get("x-9r-via-proxy")) return false; // Trusted peer IP from TCP socket (custom-server.js); unspoofable. Primary anchor for "local". const realIp = request.headers.get("x-9r-real-ip"); if (realIp) {