Deblan blog

[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'].


  • hucste
    • ,
    • 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

  • Simon
    • ,
    • 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.

  • hucste
    • ,
    • 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) { ... }

      ;)

  • Paul
    • ,
    • 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

  • Simon
    • ,
    • 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>
      

Ajouter un commentaire

Vous pouvez utiliser du markdown. Afficher l'aide.