client IP is the last X-Forwarded-For hop, not the first
NPM sets the header with $proxy_add_x_forwarded_for, which appends the connecting address to whatever the client sent. The first hop is therefore client-controlled and the last is the one NPM vouches for. Reading the first hop would have let an outsider claim a LAN address with one header, and admin_api.client_is_lan decides on it. Found while verifying the nginx block removal; fixed before the block came out was relied on. Also tightens login throttling, which used the same helper.
This commit is contained in:
+11
-2
@@ -42,10 +42,19 @@ def _render(title, body):
|
||||
|
||||
def _client_ip(request):
|
||||
"""Real client address. The app sits behind NPM, so request.client.host is
|
||||
the proxy on every hit and would throttle the whole site as one address."""
|
||||
the proxy on every hit and would throttle the whole site as one address.
|
||||
|
||||
The LAST hop, not the first. NPM sets the header with
|
||||
$proxy_add_x_forwarded_for, which APPENDS the connecting address to
|
||||
whatever the client sent, so the first hop is client-controlled and the
|
||||
last is the one NPM vouches for. With exactly one trusted proxy in front
|
||||
(the app is published on 127.0.0.1 and the docker network only), the
|
||||
rightmost address is the real client. Reading the first hop would let an
|
||||
outsider claim a LAN address with one header - and that is exactly what
|
||||
admin_api.client_is_lan decides on."""
|
||||
fwd = request.headers.get("x-forwarded-for", "")
|
||||
if fwd:
|
||||
return fwd.split(",")[0].strip()
|
||||
return fwd.split(",")[-1].strip()
|
||||
return request.client.host if request.client else None
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user