From b1d3e255e0b96d7b9700099e2ddbfd134c9340b2 Mon Sep 17 00:00:00 2001 From: vojta Date: Wed, 5 Aug 2026 12:05:04 +0200 Subject: [PATCH] =?UTF-8?q?Respektovat=20X-Forwarded-For=20za=20reverzn?= =?UTF-8?q?=C3=AD=20prox=C3=AD=20(Nginx=20Proxy=20Manager)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bez TRUST_PROXY_HEADERS=true by rate limiting viděl u každého požadavku IP proxy místo skutečného návštěvníka a limit by se sdílel napříč všemi uživateli. Přidán i návod na zapojení do Nginx Proxy Manageru v README. --- .env.example | 7 +++++++ README.md | 41 +++++++++++++++++++++++++++++++++++------ includes/functions.php | 21 +++++++++++++++++---- 3 files changed, 59 insertions(+), 10 deletions(-) diff --git a/.env.example b/.env.example index ef972bc..69ae38b 100644 --- a/.env.example +++ b/.env.example @@ -30,3 +30,10 @@ REG_PASSWORD_MAX=16 # Kolik pokusů o registraci povolit z jedné IP v daném časovém okně (vteřiny) REG_RATE_LIMIT_MAX=5 REG_RATE_LIMIT_WINDOW=3600 + +# --- Reverzní proxy (Nginx Proxy Manager, Cloudflare, ...) --- +# Nastav na "true" JEN pokud je tenhle web dostupný z internetu výhradně +# přes reverzní proxy (Apache/PHP-FPM port není veřejně dosažitelný přímo). +# Jinak si útočník IP jednoduše vymyslí přes hlavičku a rate limiting/logy +# přestanou dávat smysl. +TRUST_PROXY_HEADERS=false diff --git a/README.md b/README.md index 6d254d3..a15f69c 100644 --- a/README.md +++ b/README.md @@ -54,14 +54,16 @@ salt/verifierem, takže se hráč může rovnou přihlásit ve hře. 3. Nastav webserver tak, aby **DocumentRoot mířil na `public/`**, ne na kořen projektu — `includes/`, `data/` a `.env` tak nebudou z webu dostupné vůbec. - Příklad Apache vhost: + Příklad Apache vhost (port 80 — HTTPS řeší reverzní proxy před tímhle + serverem, viz sekce níže; pokud Apache vystavuješ na internet přímo bez + proxy, dej sem `*:443` a přidej SSL certifikát): ```apache - + ServerName wow.minkey.cz - DocumentRoot /cesta/k/wow-registrace/public + DocumentRoot /var/www/wow-registrace/public - + AllowOverride All Require all granted @@ -73,9 +75,9 @@ salt/verifierem, takže se hráč může rovnou přihlásit ve hře. ```nginx server { - listen 443 ssl http2; + listen 80; server_name wow.minkey.cz; - root /cesta/k/wow-registrace/public; + root /var/www/wow-registrace/public; index index.php; location / { @@ -120,6 +122,33 @@ salt/verifierem, takže se hráč může rovnou přihlásit ve hře. 6. Otevři stránku v prohlížeči, vyzkoušej registraci, ověř že se v `account` tabulce objevil nový řádek a že se přihlásíš herním klientem. +## Nasazení za reverzní proxy (Nginx Proxy Manager, Cloudflare, ...) + +Pokud HTTPS řeší proxy před tímto serverem (např. Nginx Proxy Manager), +Apache/Nginx tady běží jen na HTTP (viz vhosty výše) a proxy dotazy +předává dál. Je potřeba: + +1. V Nginx Proxy Manageru přidat nový **Proxy Host**: + - Domain Names: `wow.minkey.cz` + - Forward Hostname/IP + Port: adresa a port `80`, na kterém poslouchá + Apache/Nginx s tímhle vhostem (stejně, jak už máš nastavené pro + PlayerMap, jen jiná doména) + - Na záložce SSL: **Request a new SSL Certificate** (Let's Encrypt) + + zapnout **Force SSL** + +2. V `.env` nastavit: + + ``` + TRUST_PROXY_HEADERS=true + ``` + + Bez tohohle by aplikace u každého požadavku viděla jako "IP klienta" + IP samotné proxy, a rate limiting proti spamu by tak omylem počítal + všechny návštěvníky jako jednoho. **Zapínej to jen tehdy, když Apache + port 80 na tomhle serveru není z internetu dostupný přímo** (jen skrz + tu proxy) — jinak si útočník IP adresu v hlavičce jednoduše vymyslí a + rate limiting/logy přestanou dávat smysl. + ## Aktualizace ```bash diff --git a/includes/functions.php b/includes/functions.php index af381af..dc7d12d 100644 --- a/includes/functions.php +++ b/includes/functions.php @@ -3,10 +3,23 @@ declare(strict_types=1); function client_ip(): string { - // Uprav podle své infrastruktury: pokud web běží za reverzní proxy - // (nginx proxy_pass, Cloudflare, ...), musíš X-Forwarded-For nastavit - // a validovat důvěryhodně na úrovni proxy, jinak si ji klient může - // vymyslet sám. Bez proxy je REMOTE_ADDR spolehlivý zdroj. + // Za reverzní proxy (Nginx Proxy Manager, Cloudflare, ...) je + // REMOTE_ADDR vždy IP proxy, ne návštěvníka — rate limiting by tak + // omylem sdílel jeden limit pro úplně všechny. TRUST_PROXY_HEADERS=true + // v .env zapíná čtení X-Forwarded-For; zapínej ho JEN pokud je tenhle + // web dostupný z internetu výhradně přes tu proxy (Apache port 80 by + // neměl být veřejně dosažitelný přímo), jinak si klient IP jednoduše + // vymyslí a rate limiting úplně obejde. + if (env('TRUST_PROXY_HEADERS', 'false') === 'true') { + $forwardedFor = $_SERVER['HTTP_X_FORWARDED_FOR'] ?? ''; + if ($forwardedFor !== '') { + $first = trim(explode(',', $forwardedFor)[0]); + if (filter_var($first, FILTER_VALIDATE_IP)) { + return $first; + } + } + } + $ip = $_SERVER['REMOTE_ADDR'] ?? '0.0.0.0'; return filter_var($ip, FILTER_VALIDATE_IP) ? $ip : '0.0.0.0'; }