Filtrer la connexion sur l’adresse IP

Lorsqu’un site WordPress est géré depuis des postes de travail fixes, il est possible d’installer un filtre sur l’adresse IP appelante.
Seules les demandes de connexion à l’administration provenant d’une adresse IP appartenant à une liste prédéfinie sont acceptées.

Se connecter à un site WordPress

Se connecter à un site WordPress, c’est accéder à l’administration.
Un utilisateur doit avoir été déclaré avec des droits attribués : un rédacteur peut écrire et publier des articles, un administrateur peut paramétrer le site….

Voici la page d’administration.
Elle permet d’accéder au paramétrage du site : thème utilisé, définition des utilisateurs, création d’articles, gestion des médias…
Les options proposées dépendent des droits de l’utilisateur.

Connaître son adresse IP

Pour connaître l’adresse de l’équipement depuis lequel on cherche à se connecter au site :

  1. saisir dans un navigateur l’adresse https://www.whatismyip.com/fr/,
  2. l’adresse IP V6 s’affiche (si IP V6 est activé),
  3. l’adresse IP V4 s’affiche en dessous.

Récupérer l’adresse IP au niveau du site

En PHP, l’adresse IP de l’équipement utilisé pour accéder au site peut être généralement récupérée dans la variable $_SERVER[‘REMOTE_ADDR’].

Malheureusement, ce n’est pas le cas quand on utilise un CDN (Content Delivery Network).

La documentation PHP fournit le code nécessaire pour récupérer l’adresse IP quelle que soit l’infrastructure :

<?php
/*
Sometimes you will find that your website will not get the correct user IP after adding CDN, then this function will help you
*/
function real_ip()
{
   $ip = $_SERVER['REMOTE_ADDR'];
    if (isset($_SERVER['HTTP_X_FORWARDED_FOR']) && preg_match_all('#\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}#s', $_SERVER['HTTP_X_FORWARDED_FOR'], $matches)) {
        foreach ($matches[0] AS $xip) {
            if (!preg_match('#^(10|172\.16|192\.168)\.#', $xip)) {
                $ip = $xip;
                break;
            }
        }
    } elseif (isset($_SERVER['HTTP_CLIENT_IP']) && preg_match('/^([0-9]{1,3}\.){3}[0-9]{1,3}$/', $_SERVER['HTTP_CLIENT_IP'])) {
        $ip = $_SERVER['HTTP_CLIENT_IP'];
    } elseif (isset($_SERVER['HTTP_CF_CONNECTING_IP']) && preg_match('/^([0-9]{1,3}\.){3}[0-9]{1,3}$/', $_SERVER['HTTP_CF_CONNECTING_IP'])) {
        $ip = $_SERVER['HTTP_CF_CONNECTING_IP'];
    } elseif (isset($_SERVER['HTTP_X_REAL_IP']) && preg_match('/^([0-9]{1,3}\.){3}[0-9]{1,3}$/', $_SERVER['HTTP_X_REAL_IP'])) {
        $ip = $_SERVER['HTTP_X_REAL_IP'];
    }
    return $ip;

}
echo real_ip();

?>

Filtrer sur une IP V4

L’adresse IP V4 est formée de 4 nombres compris entre 0 et 255 séparés par des points. Par exemple : 192.168.1.1.

Définir les adresses acceptées

Nous commençons par créer un tableau des adresses acceptées pour accéder au site.
Nous le déclarons comme variable globale afin de pouvoir éventuellement l’utiliser dans d’autres fonctions (exemple : filtrer l’affichage de l’adresse de connexion) :

// Liste des adresses IP autorisées
global $wpdf_ip_acceptees_admin;

//	tableau des adresses IP acceptées pour se connecter à l'administration

$wpdf_ip_acceptees_admin = array (
//	'127.0.0.1',	// en local
//	'::1',	// en local
	'111.222.33.44'	//	mon IP V4
);

Les adresses locales ( 127.0.0.1 et ::1) sont utilisées dans des installations de tests comme wampserver.

Pour modifier l’adresse, il est nécessaire d’accéder au code de l’extension via une connexion FTP ou SSH et d’éditer le code. C’est une vraie contrainte, mais inévitable : on ne peut pas se connecter à l’administration… pour autoriser l’accès à l’administration.
Ce n’est acceptable que si les adresses IP ne changent pas trop souvent : stable pour votre box, lieu de connexion qui ne varie pas souvent…

Vérifier l’adresse IP

Nous utilisons l’action login_init pour ajouter le contrôle :

  1. ajout de la fonction à l’action,
  2. récupérer le tableau des adresses IP autorisées,
  3. récupérer l’adresse IP du visiteur du site,
  4. si l’adresse n’est pas contenue dans le tableau, arrêter le traitement.
//	ne pas accepter le login si l'adresse IP n'est pas déclarée	
add_action( 'login_init', function () { 	// (1)

global $wpdf_ip_acceptees_admin;	// (2)

$wpdf_adresse_ip = wpdf_real_ip();   // récupérer l'adresse IP (3)

if (!in_array ($wpdf_adresse_ip, $wpdf_ip_acceptees_admin)) wp_die('Connexion non autorisée');// (4)
		
	}	//	fin fonction
);	//	fin add_action sur login_init

Filtrer sur une IP V6

IP V6 et préfixe

Une adresse IPv6 est longue de 128 bits et se compose de huit champs de 16 bits, chacun étant délimité par deux-points (:). Chaque champ comporte 4 chiffres hexadécimaux : 0 à 9 et a à f. Par exemple : 2001:0db8:0000:85a3:0000:0000:ac1f:8001

L’adresse est découpée en trois parties : le préfixe, l’Identifiant de sous-réseau et l’identifiant d’interface.

Cas d’une box

Pour une box, la partie ID d’interface varie dans le temps et en fonction de l’équipement (ordinateur, tablette, smartphone).
On ne peut donc pas tester sur la totalité de l’adresse IP V6, il faudrait mettre à jour en permanence le tableau des adresses autorisées.

Il faut se rendre dans l’administration pour obtenir des précisions sur l’adresse IP V6.
Voici un exemple réel :

L’information importante est le préfixe IP V6. Ce préfixe désigne le début d’adresse IP qui sera commun à tout équipement connecté à la box.
Le nombre de bits de ce préfixe se trouve en fin d’adresse (ici : 56).

Chaque chiffre de l’adresse IP V6 correspondant à 4 bits (chiffre hexadécimal), le préfixe correspond donc à 56 / 4 = 14 chiffres.
Chaque groupe séparé par deux points contenant 4 chiffres, il faut donc prendre les trois premiers groupes et les deux premiers chiffres du quatrième groupe. Cela correspond au 17 premiers caractères car il faut prendre en compte les deux points séparateurs de groupes :

4 => premier groupe
+ 1 => ‘:’
+ 4 => deuxième groupe
+ 1 => ‘:’
+ 4 => troisième groupe
+ 1 => ‘:’
+ 2 => quatrième groupe
= 17

Si le préfixe est ‘1111:2222:3333:4444::/56‘, il faudra vérifier que l’adresse IP V6 de l’équipement qui se connecte commence par ‘1111:2222:3333:44‘, ce qui fait bien 17 caractères.

Si le préfixe se composait de 48 bits, il suffirait de prendre les 3 premiers groupes de chiffres et 14 caractères (4 + 1 + 4 + 1 + 4).

Récupérer le préfixe de l’adresse IP V6

Nous modifions la fonction de récupération de l’adresse IP :

  1. en déclarant en variable globale le nombre de caractères du préfixe IP V6 (17 dans notre exemple),
global $wpdf_ip_acceptees_admin,$wpdf_taille_prefixe_ipv6;

// taile du préfixe IPV6 ':' compris
$wpdf_taille_prefixe_ipv6 = 17;
  1. en restreignant l’adresse IP aux premiers caractères correspondants au préfixe
// préparer la recherche si IP V6
if (strpos ($wpdf_adresse_ip,':')!= false) {
	$wpdf_adresse_ip = substr ( $wpdf_adresse_ip, 0, $wpdf_taille_prefixe_ipv6);
	}

Ci-dessous, le code complet de l’extension :

<?php
/*
Plugin Name: Filtrer IP
Author: D Farnier
AuthorURI: D Farnier
Description: N'autoriser la connexion au site que si l'IP du demandeur ne fait pas partie d'une liste prédéfinie
*/

if ( !defined('ABSPATH') ) exit ('Faites demi-tour !'); // Bloquer si la fonction n'est pas appelée depuis WordPress

// Liste des adresses IP autorisées
global $wpdf_ip_acceptees_admin,$wpdf_taille_prefixe_ipv6;

// taile du préfixe IPV6 ':' compris
$wpdf_taille_prefixe_ipv6 = 17;	

//	tableau des adresses IP acceptées pour se connecter à l'administr&tion

global $wpdf_ip_acceptees_admin;
$wpdf_ip_acceptees_admin = array (
	'127.0.0.1',			// en local
	'::1',					// en local
	'111.222.33.44'			//	mon IP V4
	'2a01:cb1d:85c0:c5',	//	mon IP V6
);

// récupération de l'adresse IP (voir : https://www.php.net/manual/fr/reserved.variables.server.php#122495)
function wpdf_real_ip()
{
	global $wpdf_taille_prefixe_ipv6;
	
	$wpdf_adresse_ip = $_SERVER['REMOTE_ADDR'];
    if (isset($_SERVER['HTTP_X_FORWARDED_FOR']) && preg_match_all('#\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}#s', $_SERVER['HTTP_X_FORWARDED_FOR'], $matches)) {
        foreach ($matches[0] AS $xip) {
            if (!preg_match('#^(10|172\.16|192\.168)\.#', $xip)) {
                $wpdf_adresse_ip = $xip;
                break;
            }
        }
    } elseif (isset($_SERVER['HTTP_CLIENT_IP']) && preg_match('/^([0-9]{1,3}\.){3}[0-9]{1,3}$/', $_SERVER['HTTP_CLIENT_IP'])) {
        $wpdf_adresse_ip = $_SERVER['HTTP_CLIENT_IP'];
    } elseif (isset($_SERVER['HTTP_CF_CONNECTING_IP']) && preg_match('/^([0-9]{1,3}\.){3}[0-9]{1,3}$/', $_SERVER['HTTP_CF_CONNECTING_IP'])) {
        $wpdf_adresse_ip = $_SERVER['HTTP_CF_CONNECTING_IP'];
    } elseif (isset($_SERVER['HTTP_X_REAL_IP']) && preg_match('/^([0-9]{1,3}\.){3}[0-9]{1,3}$/', $_SERVER['HTTP_X_REAL_IP'])) {
        $wpdf_adresse_ip = $_SERVER['HTTP_X_REAL_IP'];
    }
	// préparer la recherche si IP V6
	if (strpos ($wpdf_adresse_ip,':')!= false) {
		$wpdf_adresse_ip = substr ( $wpdf_adresse_ip, 0, $wpdf_taille_prefixe_ipv6);
	}
    return $wpdf_adresse_ip;
}

//	ne pas accepter le login si l'adresse ip n'est pas déclarée	
add_action( 'login_init', function () { 	

global $wpdf_ip_acceptees_admin;

$wpdf_adresse_ip = wpdf_real_ip();	// récupérer l'adresse IP

		if (!in_array ($wpdf_adresse_ip, $wpdf_ip_acceptees_admin)) wp_die('Connexion non autorisée');
		
	}	//	fin fonction  vérification des ip pour administration
);	//	fin add_action sur login_init

?>

Vous pouvez cliquer en haut et à droite sur l’icône permettant d’afficher le code sous une forme facilitant le copier / coller.
L’icône apparaît lorsqu’on survole le code.