Respektovat X-Forwarded-For za reverzní proxí (Nginx Proxy Manager)

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.
This commit is contained in:
2026-08-05 12:05:04 +02:00
parent 8763e245f5
commit b1d3e255e0
3 changed files with 59 additions and 10 deletions
+7
View File
@@ -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
+35 -6
View File
@@ -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
<VirtualHost *:443>
<VirtualHost *:80>
ServerName wow.minkey.cz
DocumentRoot /cesta/k/wow-registrace/public
DocumentRoot /var/www/wow-registrace/public
<Directory /cesta/k/wow-registrace/public>
<Directory /var/www/wow-registrace/public>
AllowOverride All
Require all granted
</Directory>
@@ -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
+17 -4
View File
@@ -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';
}