59 lines
1.5 KiB
PHP
59 lines
1.5 KiB
PHP
<?php
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Shared\Http;
|
|
|
|
use Psr\Http\Message\ServerRequestInterface;
|
|
|
|
/**
|
|
* Résout l'adresse IP cliente à partir de la requête HTTP.
|
|
*
|
|
* L'en-tête X-Forwarded-For n'est pris en compte que si REMOTE_ADDR
|
|
* correspond à un proxy explicitement approuvé. En l'absence d'IP
|
|
* exploitable, la valeur de repli '0.0.0.0' est renvoyée.
|
|
*/
|
|
final class ClientIpResolver
|
|
{
|
|
/**
|
|
* @param string[] $trustedProxies
|
|
*/
|
|
public function __construct(private readonly array $trustedProxies = [])
|
|
{
|
|
}
|
|
|
|
public function resolve(ServerRequestInterface $request): string
|
|
{
|
|
$serverParams = $request->getServerParams();
|
|
$remoteAddr = trim((string) ($serverParams['REMOTE_ADDR'] ?? ''));
|
|
|
|
if ($remoteAddr === '') {
|
|
return '0.0.0.0';
|
|
}
|
|
|
|
if (!$this->isTrustedProxy($remoteAddr)) {
|
|
return $remoteAddr;
|
|
}
|
|
|
|
$forwarded = trim((string) ($serverParams['HTTP_X_FORWARDED_FOR'] ?? ''));
|
|
|
|
if ($forwarded === '') {
|
|
return $remoteAddr;
|
|
}
|
|
|
|
$candidate = trim(explode(',', $forwarded)[0]);
|
|
|
|
return filter_var($candidate, FILTER_VALIDATE_IP) ? $candidate : $remoteAddr;
|
|
}
|
|
|
|
private function isTrustedProxy(string $remoteAddr): bool
|
|
{
|
|
foreach ($this->trustedProxies as $proxy) {
|
|
if ($proxy === '*' || $proxy === $remoteAddr) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
}
|