[tips] PHP FPM : récupérer la vraie adresse IP du visiteur
Mon serveur web fonctionne par couches. La première couche est gérée par Nginx et traite les requetes HTTP des internautes. Nginx gère les problématiques de cache sur les assets, c'est à dire les images, les fichiers javascripts et enfin les fichiers CSS. Ensuite, il transmet les requêtes au serveur web Apache qui va délivrer le site web concerné et faire appel au process manager de PHP (FPM) pour exécuter PHP.
En l'état, il n'est pas possible de connaître l'adresse IP de l'internaute via
la variable globale $_SERVER
en utilisant l'index REMOTE_ADDR
. En effet, si j'affiche le
contenu de $_SERVER['REMOTE_ADDR']
, j'aurai comme résultat 127.0.0.1
qui est l'IP locale de Nginx.
Cependant, j'ai configuré Nginx de tel sorte qu'il ajoute les entêtes HTTP X-Forwarded-For
et X-Real-IP
.
Apache les transmet ensuite via les index HTTP_X_FORWARDED_FOR
et HTTP_X_REAL_IP
.
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $remote_addr;
Ces index contiennent l'IP de l'internaute mais ne sont pas utilisés par les applications hébergées.
Typiquement, Nextcloud détectait l'adresse IP 127.0.0.1
. Lors d'un brute force, tous les comptes utilisateurs
étaient impactés par la sécurité enclanchée par Nextcloud (soit 30s d'attente avant la validation des identifiants de connexion).
J'ai fouiné sur la toile et plusieurs solutions sont évoquées. J'ai un peu tout essayé et au delà des modules Apache Rpaf et RemoteIP, pas grand chose de probant. Rpaf est déjà installé et permet à Apache de logger la bonne IP dans les logs d'accès. RemoteIP n'a pas fonctionné dans mon environnement.
Pour résoudre le problème, j'ai appliqué ce qui a été rédigé dans ce post : inclure un script PHP de façon automatique pour l'ensemble des sites web du serveur.
j'ai créé un fichier PHP comme suit :
<?php
$trustedProxies = [
'127.0.0.1',
];
$remote = $_SERVER['REMOTE_ADDR'];
$headers = [
'HTTP_X_FORWARDED_FOR' => 'REMOTE_ADDR',
'HTTP_X_REAL_IP' => 'REMOTE_HOST',
];
if (in_array($remote, $trustedProxies)) {
foreach ($headers as $header => $value) {
$_SERVER[$value] = $_SERVER[$header];
}
}
Puis j'ai alimenté les php.ini
de cette façon :
auto_prepend_file = /etc/php/fpm/real-ip.php
Après avoir relancé les services, j'ai pu constater que l'adresse IP de l'internaute est bien présente dans $_SERVER['REMOTE_ADDR']
.

Expiration de certificats SSL
Fin avril dernier, j'ai publié un projet qui génère un récapitulatif de la date d'expiratio…

Outil de création d’un espace web Apache et PHP
Dans mon activité personnelle et professionnelle, je suis amené à créer des espaces d’héber…

API pour récupérer le contenu Open Graph d'une page web
Pour la fonctionnalité de partage de liens sur ce blog, j'ai développé un script qui récupé…
Légèrement plus simple : - la variable $trustedProxies devient de type string en ne contenant que la chaîne de caractères '127.0.0.1'. - la variable $remote reste telle quelle... - le tableau $headers reste :p
Et, au lieu de faire un traitement in_array() consommateur de ressource inutilement dans ce cas-là, il suffit de faire une comparaison de chaîne, tel que :
if(str_cmp($trustedProxies, $remote) === 0)) { foreach(...) { ... } # reprendre le même code ;) }
Voili, voilou... :p :D
3 centièmes sur 10 000 000 itérations, j’admets la différence de performance mais rien de bien significatif :) Dans le cas d'une machine qui sait parler en IPv6 et qui le ferait en interne,
::1
pourrait être ajouté à127.0.0.1
. Du coup,in_array
devient plus apriorité je pense.
Même pas sûr ;)
soit :
if(str_cmp('127.0.0.1', $_SERVER['REMOTE_ADDR']) === 0) || str_cmp('::1', $_SERVER['REMOTE_ADDR']) === 0)) { ... }
il serait même intéressant d'utiliser la fonction preg_match(), telle que :
if(preg_match('/(127.0.0.1)(::1)/', $_SERVER['REMOTE_ADDR']) === 0) { ... }
;)
Bonjour, j'utilise moi aussi php-fpm (chrooté d'ailleurs) mais en utilisant un socket et non pas 127.0.0.1, $_SERVER["REMOTE_ADDR"] est rempli correctement (cliquer sur mon nom pour voir une URL d'exemple). Bonne journée
Via apache ? De mon coté, c'est configuré de cette façon :
<VirtualHost …> … <IfModule mod_fastcgi.c> AddHandler php-fcgi .php Action php-fcgi /php-fcgi Alias /php-fcgi /usr/lib/cgi-bin/php-fcgi-webdeblanio-444 FastCgiExternalServer /usr/lib/cgi-bin/php-fcgi-webdeblanio-444 -idle-timeout 250 -host 127.0.0.1:17092 -pass-header Authorization </IfModule> … </VirtualHost>
Bonjour, vous trouverez un exemple de conf avec FastCgiExternalServer + un socket sur le lien de ce message. Mais je suis passé depuis à ProxyPassMatch + un socket, pour une configuration plus concise sans le AddHandler/AddType ni le Action.