Selecteur de langue

Ma check-list de sécurité avec laquelle je travaille régulièrement. J’y compile un ensemble d’actions manuelles à effectuer pour renforcer la sécurité d’un site internet WordPress. C’est celle que j’utilise moi-même pour les sites dont je m’occupe au quotidien. Elle est non exhaustive et peut-être amenée à évoluer au fil du temps, mais elle te donnera une bonne base sur laquelle travailler.

Cette check-list de sécurité s’appuie sur mon approche personnelle, à savoir proposer des solutions granulaires et légères. Aussi, tu n’y trouveras pas de grosses extensions de sécurité « all-in-one », ce qui peut très bien faire le job, mais qui sera aussi nécessairement plus lourd. Mon idée, c’est te proposer une liste d’actions immédiatement applicables qui renforceront la sécurité de ton site, en partant du principe qu’il n’est pas déjà compromis. Auquel cas, il te faudra à minima un outil incluant un scan anti-virus.

Maintenant que cela est dit, renforçons ton site !

La base de la base

  • Le facteur humain ! Assure-toi de respecter les règles de base pour la sécurité numérique, en particulier la première : un mot de passe robuste par accès. Et ne partage pas tes mots de passe par e-mail.
  • Adresse-toi à un hébergeur de qualité et sécurisé, s’il propose des services conçus spécifiquement pour WordPress c’est encore mieux. Certaines des bonnes pratiques listées ici seront déjà en place.
  • Met à jour (très très) régulièrement WordPress ! Si tu n’as pas de prestataire de maintenance qui s’en occupe, active à minima les mises à jour automatiques des versions mineures.
  • Met à jour (très très) régulièrement les extensions et les thèmes pour garantir que toutes les extensions restent sécurisées.
  • Assure-toi de sauvegarder ton site quotidiennement. Beaucoup d’hébergeurs proposent des sauvegardes automatiques, pense à les activer.
  • Limite le nombre d’utilisateurs administrateurs. Un grand pouvoir implique de grandes responsabilités. Les administrateurs des sites doivent être formés à l’usage de l’interface d’administration et être informés en cas de modifications. Quelqu’un doit simplement s’occuper du contenu ? Le rôle d’éditeur est là pour ça.
  • Évite d’utiliser des thèmes/extensions piratés (dites no no aux trucs « nulled » ou « GPL Free »). D’un point de vue strictement sécuritaire, à part si tu es capable d’auditer toi-même le plugin wp-rocket nulled que tu as payé 5 euros, c’est une très mauvaise idée d’installer une extension qui a été modifiée par un tiers auquel tu ne peux pas faire confiance. C’est un vecteur de piratage très courant.

. htaccess – Limiter les accès indésirables et sécuriser les fichiers critiques.

Désactiver le listing des fichiers

En désactivant le listing des fichiers, tu empêches les visiteurs de voir la liste complète des fichiers présents dans les répertoires (les dossiers).

Un répertoire comme /wp-content/uploads/ contient des fichiers téléversés, sans cette règle, un attaquant pourrait accéder à l’URL https://ton-site.com/wp-content/uploads/ et voir tous les fichiers. Avec la directive Options -Indexes dans .htaccess, cette action retournera une erreur 403 (Accès interdit) au lieu d’afficher le contenu du dossier.

# Directories read protection
Options -IndexesLangage du code : Apache (apache)

Bloquer l’accès aux fichiers critiques

Protège les fichiers sensibles qui contiennent les informations techniques et les configurations de ton site WordPress.

En détails :

  • .htaccess : Contrôle des aspects importants du serveur. S’il est modifié ou accessible, un attaquant pourrait désactiver des protections ou insérer des directives malveillantes.
  • wp-config.php : Contient des informations vitales : comme les identifiants de connexion à la base de données.
  • readme.txt : Ces fichiers peuvent révéler la version exacte de ton site WordPress ou des extensions que tu utilises. Ce qui facilite les attaques ciblées.
# Protect .htaccess file
<files .htaccess>
order allow,deny
deny from all
</files>

# Protect wp-config.php file
<files wp-config.php>
order allow,deny
deny from all
</files>

# Redirect Readme file(s) access to 404
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteRule (/|^)(readme|changelog|license)\.(txt|html|md)$ - [R=404,L,NC]
</IfModule>Langage du code : Apache (apache)

Bloquer l’accès aux fichiers de développement

Ces fichiers peuvent contenir des informations sensibles comme des erreurs PHP, des configurations ou des données utilisateur.

# Blocks access to error_log files containing PHP errors
<Files ~ "error_log">
Deny from all
</Files>

# Blocks access to database files
<Files ~ "\.(sql\.gz|sql\.zip|sql)$">
Deny from all
</Files>

# Block direct access to development files
<FilesMatch "^\.(env|git|log|bak)$">
Order Allow,Deny
Deny from all
</FilesMatch>Langage du code : AngelScript (angelscript)

Masquer la version de WordPress

Les attaquants utilisent souvent des outils pour détecter les versions précises de WordPress installées sur un site. Si une version spécifique est connue pour contenir des failles de sécurité, cela facilite les attaques ciblées. En cachant cette information, vous rendez votre site moins vulnérable à ces tentatives.

Par défaut, WordPress inclut des informations sur sa version dans l’en-tête HTTP sous la forme X-Powered-By ou d’autres métadonnées visibles. Cette règle permet simplement de l’enlever.

# Hide WordPress version in HTTP head
<IfModule mod_headers.c>
Header unset X-Powered-By
</IfModule>Langage du code : Apache (apache)

Bloquer l’accès à xmlrpc.php

xmlrpc permet à des applications externes de communiquer avec ton site, par exemple un logiciel de publication externe à l’éditeur du site. C’est une fonctionnalité activée par défaut qu’on utilise rarement sur des sites récents. Elle peut être une porte d’entrée utilisée pour lancer des attaques comme force brute ou DDoS. Cette règle permet de bloquer l’accès au fichier gérant cette fonctionnalité. Tu trouveras également un petit extrait de code pour désactiver la fonctionnalité dans WordPress.

# Blocks access to the xmlrpc file
<Files xmlrpc.php>
order deny,allow
deny from all
</Files>Langage du code : Apache (apache)

Forcer une redirection HTTPS

Assure que toutes les connexions au site utilisent le protocole sécurisé, protégeant ainsi les données échangées entre le serveur et les utilisateurs contre les interceptions. Cela empêche les visiteurs d’accéder au site en utilisant une connexion non sécurisée (HTTP), ce qui pourrait exposer leurs informations sensibles à des attaques de type « man-in-the-middle ».

Un cas d’usage typique serait un site e-commerce où des informations sensibles, telles que des identifiants de connexion ou des détails de paiement, sont échangées. Forcer la redirection HTTPS renforcera la confidentialité et la sécurité des transactions.

# Force HTTPS
<IfModule mod_rewrite.c>
RewriteEngine On 
RewriteCond %{HTTP:X-Forwarded-Proto} !https
RewriteCond %{HTTPS} !on
RewriteRule ^(.*) https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
</IfModule>Langage du code : Apache (apache)

Bonus SEO

Forcer le trailing slash sur les URL sans extension

Permet d’uniformiser la structure des liens du site. Cette pratique évite des confusions pour les moteurs de recherche, qui pourraient considérer les URL avec et sans slash comme deux pages avec exactement le même contenu, ce qui « diluerait » le SEO entre les deux.

Ainsi, un lien comme « https://exemple.com/page » sera redirigé vers « https://exemple.com/page/ » garantissant l’accès à une seule et même page.

# Force trailing slashes
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_METHOD} GET
RewriteCond %{REQUEST_URI} !(.*)/$
RewriteRule ^(.*)$ https://%{HTTP_HOST}/$1/ [L,R=301]
</IfModule>Langage du code : Apache (apache)

Forcer la redirection du sous domaine WWW

Forcer la redirection du sous-domaine WWW permet d’uniformiser l’accès au site, en redirigeant soit toutes les requêtes vers la version avec WWW, soit vers la version sans WWW.

Choisis selon ton cas d’usage, site en WWW ou non, attention à bien saisir ton nom de domaine à la place de name.domain.

# Redirect www to non-www
<IfModule mod_rewrite.c>
RewriteEngine on
RewriteCond %{HTTP_HOST} ^www\.name\.domain [NC]
RewriteRule ^(.*)$ https://name.domain/$1 [L,R=301]
</IfModule>Langage du code : Apache (apache)
# Redirect non-www to www
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteCond %{HTTP_HOST} ^name.domain
RewriteRule (.*) https://www.name.domain/$1 [L,R=301]
</IfModule>Langage du code : Apache (apache)

wp-config.php – Renforcer la configuration de WordPress.

Empêcher l’affichage des erreurs

Permet d’éviter la divulgation d’informations sensibles sur la configuration du serveur ou la structure du site.

define('WP_DEBUG', false);
define('WP_DEBUG_LOG', false);
define('WP_DEBUG_DISPLAY', false);
define('SCRIPT_DEBUG', false);Langage du code : PHP (php)

Désactiver l’éditeur de fichiers

Renforce la sécurité en empêchant les utilisateurs (ou attaquants) de modifier les fichiers de thèmes et d’extensions via l’interface d’administration.

Par exemple, si un attaquant prend le contrôle d’un compte administrateur, il peut facilement accéder à l’éditeur de fichiers pour injecter un code malveillant ou modifier des fichiers critiques.

define('DISALLOW_UNFILTERED_HTML', true); //PEUT CASSER LES SVG SUR L'INTERFACE PUBLIQUE
define('DISALLOW_FILE_EDIT', true);Langage du code : PHP (php)

Forcer la désactivation de la page de réparation

Force la désactivation de la page de réparation pour empêcher un accès non autorisé à l’outil intégré de WordPress qui permet de réparer et de réparer la base de données. Cette page pourrait exposer des informations sensibles si elle est accessible publiquement, notamment des détails sur la structure de la base de données.

define('WP_ALLOW_REPAIR', false);Langage du code : PHP (php)

Filtrer les téléversements

Filtre les téléversements pour contrôler les types de fichiers qui peuvent être téléchargés sur le site, limitant ainsi les risques d’exécution de fichiers malveillants.

Sans ce filtrage, un attaquant pourrait télécharger un fichier PHP déguisé en image, puis l’exécuter sur le serveur pour obtenir un accès non autorisé.

define('ALLOW_UNFILTERED_UPLOADS', false);Langage du code : PHP (php)

Empêcher le changement de nom de domaine du site

Empêcher le changement de nom de domaine du site permet de verrouiller les paramètres de l’URL et d’empêcher toute modification non autorisée de l’adresse du site dans l’administration de WordPress. Cela peut être crucial pour éviter qu’un sattaquant modifie les URL de ton site afin de rediriger les visiteurs vers un site malveillant.

define('WP_ENVIRONMENT_TYPE', 'production');
define('WP_HOME', 'https://name.domain/');
define('WP_SITEURL', 'https://name.domain/');Langage du code : PHP (php)

Forcer la connexion sécurisée à l’interface d’administration

Force la connexion sécurisée à l’interface d’administration pour assurer que l’ensemble des échanges entre l’utilisateur et le site se fait uniquement via HTTPS. Cela protège les informations sensibles, comme les identifiants de connexion et les actions administratives, contre les interceptions potentielles lors de la transmission de données.

define('FORCE_SSL_LOGIN', true);
define('FORCE_SSL_ADMIN', true);Langage du code : PHP (php)

Forcer les mises à jour automatique mineures de WordPress

Permet de garantir que ton site WordPress applique rapidement les correctifs de sécurité et les petites améliorations sans intervention manuelle. Cela aide à réduire les risques associés aux vulnérabilités découvertes dans les versions antérieures, minimisant ainsi la fenêtre d’opportunité pour les attaquants. J’en profite pour désactiver le téléchargement automatique des nouveaux thèmes par défaut de WordPress qui ne m’intéressent pas sur un site en production.

define('WP_AUTO_UPDATE_CORE', 'minor');
define('CORE_UPGRADE_SKIP_NEW_BUNDLED', true);Langage du code : PHP (php)

Configurer des clés de sécurité

Configure les clés de sécurité de ton site renforce la protection des sessions utilisateurs en cryptant les cookies de connexion et de session. Ces clés uniques et secrètes rendent les cookies inutilisables si un attaquant parvient à les voler, car sans ces clés, le cookie ne pourra pas être authentifié correctement.

Remplacer cette partie dans ton fichier avec celle générée ici : https://api.wordpress.org/secret-key/1.1/salt/

define('AUTH_KEY',         ']AP.n.;g2t}^H^W ~&-19},0g+]*XnJsG2{dd!.2l|`$s>$<8%7J$.UK s7L>+Cj');
define('SECURE_AUTH_KEY',  'RLNn^ &ns#Ady!Xlg!N|AstpB?_Yen T}sc;$&wS+^T&%+~E *c]1pie.eTmyZHZ');
define('LOGGED_IN_KEY',    'H]BJj1~XK?z<+##=Y/+wuUrT:!*.YWleLxZ>_0XO@J3>3(eyJY8rR,4V@h==I|Y;');
define('NONCE_KEY',        'iLCnH=E$pdU4il%*7Fjq^Y3N7fD^o|5;,C*-.1dyp-?A;*cA>;8J0EipnDNH4sHb');
define('AUTH_SALT',        '&-.WI#@UJj4BQi@T={^m][L</8BWIHn)ZeF#5 .#vnO%cC_6dEcn,JgMZXXv/m6t');
define('SECURE_AUTH_SALT', '%A$*vRs1BKlZ@Lb<1R;||u|UC`%9a]-cKis#AL<cS~.^[pkZZ/G[zOY&6KDzL)2S');
define('LOGGED_IN_SALT',   '-XLU6pmXcH(73_cGw-msP2)PhuYO>>$SR`3IY1--3Z,|OtHM1Z%|XC%?#(TAKC}/');
define('NONCE_SALT',       'b-OIt6B8^xy!{N/Q`PxmzwdIfK)/oiq%gM,^r(bAl|`nr/)>^M0qr{[i ?ZR8$xN');Langage du code : PHP (php)

functions.php – Modifier ou désactiver des comportements par défaut de WordPress.

Masquer les erreurs de connexion

Masque les erreurs de connexion pour ne pas laisser l’indice aux attaquants de savoir si le nom d’utilisateur ou le mot de passe est incorrect. En affichant un message générique comme « erreur de connexion », tu réduis les informations exploitables pour une attaque par force brute.

// Remove login error message
add_filter('login_errors', function() {
    return 'Erreur de connexion.';
});Langage du code : PHP (php)

Désactiver le XML-RPC

Dans le .htaccess j’ai mis une règle qui bloque l’accès au fichier xmlrpc.php. Cet extrait de code désactive la fonctionnalité associée dans WordPress.

// Disable XMLRPC
add_filter('xmlrpc_enabled', '__return_false');
remove_action('wp_head', 'rsd_link');Langage du code : PHP (php)

Désactiver les pingbacks et trackbacks

Ces fonctionnalités peuvent être utilisées pour envoyer des spams ou, dans des cas plus graves, pour mener des attaques DDoS en utilisant le site victime comme relais pour bombarder un autre site avec des requêtes. En fermant l’accès à ces notifications, on empêche qu’un attaquant exploite le site pour envoyer massivement des requêtes.

// Remove X-Pingback headers
add_filter('wp_headers', 'zenpress_remove_x_pingback');
function zenpress_remove_x_pingback($headers) {
    unset($headers['X-Pingback']);

    return $headers;
}

// Disable pingbacks and trackbacks for new articles
add_action('after_setup_theme', 'zenpress_disable_pingback_and_trackback');
function zenpress_disable_pingback_and_trackback() {
    update_option('default_ping_status', 'closed');
    update_option('default_pingback_flag', 0);
}

// Self Pingbacks
add_action('pre_ping', function (&$links) {
    $home = get_option('home');
    foreach ($links as $l => $link) {
        if (strpos($link, $home) === 0) {
            unset($links[$l]);
        }
    }
});Langage du code : PHP (php)

Masquer la version de WordPress et WooCommerce

remove_action('wp_head', 'wp_generator');

add_filter('the_generator', 'zenpress_remove_wordpress_version');
function zenpress_remove_wordpress_version() {
    return '';
}

// Remove WordPress version from scripts and styles
add_filter('style_loader_src', 'zenpress_remove_version_from_style_js');
add_filter('script_loader_src', 'zenpress_remove_version_from_style_js');
function zenpress_remove_version_from_style_js($src) {
    if (strpos($src, 'ver=' . get_bloginfo('version'))) {
        $src = remove_query_arg('ver', $src);
    }

    return $src;
}Langage du code : PHP (php)
if (class_exists('woocommerce') && !is_admin()) {
    // Hide WooCommerce version from HTTP headers
    add_filter('wp_headers', 'zenpress_remove_woocommerce_version');
    function zenpress_remove_woocommerce_version($headers) {
        if (isset($headers['X-WooCommerce-Version'])) {
            unset($headers['X-WooCommerce-Version']);
        }

        return $headers;
    }

    // Remove WooCommerce version from scripts and styles URLs
    add_filter('style_loader_src', 'zenpress_remove_woocommerce_version_scripts_styles', 10);
    add_filter('script_loader_src', 'zenpress_remove_woocommerce_version_scripts_styles', 10);
    function zenpress_remove_woocommerce_version_scripts_styles($src) {
        if (strpos($src, 'ver=') && strpos($src, 'woocommerce')) {
            $src = remove_query_arg('ver', $src);
        }

        return $src;
    }
}Langage du code : PHP (php)

Limiter les tentatives de connexion

Permet de réduire les risques d’attaques par force brute, où un attaquant tente de deviner le mot de passe en essayant de nombreuses combinaisons.

add_filter('login_errors', function () {
    return __('Login error.', 'your-theme');
});

add_filter('authenticate', 'zenpress_login_protection', 30, 3);
function zenpress_login_protection(mixed $user, string $username, string $password): mixed {
    $MAX_LOGIN_ATTEMPTS = 5;
    $BLOCK_DURATION = 30; // 5 minutes

    $ipAddress = isset($_SERVER['REMOTE_ADDR']) ?
        filter_var(wp_unslash($_SERVER['REMOTE_ADDR']), FILTER_VALIDATE_IP) : 'unknown';

    $transientKey = 'zenpress_login_block_' . $ipAddress;
    if ($user instanceof WP_User) {
        delete_transient($transientKey);

        return $user;
    }
    if (get_transient($transientKey)) {
        header('HTTP/1.0 403 Forbidden');
        die('Access Denied');
    }
    $attemptsData = get_transient('zenpress_login_attempts_' . $ipAddress);
    $attempts = $attemptsData['count'] ?? 0;
    $attempts++;
    if ($attempts > $MAX_LOGIN_ATTEMPTS) {
        set_transient($transientKey, true, $BLOCK_DURATION);
        header('HTTP/1.0 403 Forbidden');
        die('Access Denied');
    }
    set_transient('zenpress_login_attempts_' . $ipAddress, ['count' => $attempts], $BLOCK_DURATION);

    return $user;
}
Langage du code : PHP (php)

Extensions additionnelles – Protections avancées et Pare-feu

Pare-feu

Le pare-feu BBQ Firewall est conçu pour protéger les sites WordPress contre les attaques courantes, telles que les injections SQL, les attaques XSS (cross-site scripting) et d’autres formes de tentatives malveillantes. Cette extension embarque un ensemble de règles légères qui renforcent la sécurité de ton site.

➡️ BBQ Firewall – Fast & Powerful Firewall Security par Jeff Starr

Attention, ce petit pare-feu peut affecter des développements sur-mesure, il est toutefois possible de customiser la configuration de base pour répondre à votre besoin et désactiver des protections qui pourraient être bloquantes : https://perishablepress.com/customize-bbq-firewall/.

Déplacer la page de connexion (/wp-login.php)

Permet de rendre l’accès à l’interface d’administration moins évident pour les attaquants. En modifiant l’URL par défaut de ta page de connexion, qui est généralement accessible via /wp-login.php, les attaquants auront plus de mal à localiser cette porte d’entrée et à automatiser les tentatives de connexion ou d’attaques par force brute.

➡️ WPS Hide Login par WPServeur, NicolasKulka, wpformation

Renforcer les entête HTTP

L’extension Headers Security Advanced & HSTS WP permet de configurer facilement des entêtes comme X-Content-Type-Options, X-XSS-Protection, Content-Security-Policy et HTTP Strict Transport Security (HSTS), qui ajoutent des couches de protection contre les attaques courantes.

➡️ Headers Security Advanced & HSTS WP par Andrea Ferro

Sources

Auteur

Quentin Le Duff – Votre partenaire WordPress

Commentaires

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *