WordPress est le CMS le plus répandu dans le monde, il équipe plus de 24% des sites internet mondiaux. Par conséquent il est bien normal qu’il soit très ciblé par les pirates informatiques : une seule faille dans WordPress donne accès à bien des sites internet sur lesquels héberger du contenu illégal, obtenir des informations, spammer etc. Voici mon guide personnel de hardening WordPress, issu de réflexions personnelles et de méthodes trouvées sur internet.
Dernière mise à jour : décembre 2016.
Précautions sur le serveur web
Voici les réglages recommandés sur le serveur web Apache pour héberger un site internet sans trop prêter le flanc aux attaques.
Être discret sur la version du serveur
Si les pirates connaissant la version exacte du serveur web et du système d’exploitation, il leur suffit d’aller chercher des exploits qui fonctionneront. Ne pas leur donner cette information est déjà un premier pas. Les réglages ServerSignature et ServerTokens de Apache permettent de ne pas s’étaler ni dans le footer des pages d’erreur du serveur web (404 par exemple), ni dans les headers HTTP.
1 2 3 4 |
root@ordinateur:~# vi /etc/apache2/conf.d/security ServerSignature Off ServerTokens Prod root@ordinateur:~# service apache2 restart |
Cette commande requiert l’accès ligne de commande à votre serveur, comme par exemple si vous avez un serveur dédié.
Si vous utilisez un hébergement mutualisé, la commande ServerSignature Off peut également être ajoutée dans votre .htaccess.
Ne pas lister le contenu des fichiers
Le comportement par défaut d’apache lorsqu’il ne trouve pas de page index.html ou index.php, c’est d’afficher le contenu du dossier comme un explorateur de fichiers. Ce n’est pas bon du tout. Le module autoindex est responsable de ce désastre et le plus radical est de le supprimer.
1 2 |
root@ordinateur:~# a2dismod autoindex root@ordinateur:~# service apache2 restart |
Si ce module vous est utile, alors vous pouvez demander sa désactivation sur certains dossiers. Voici la règle à inclure dans la configuration apache (/etc/apache2/sites-available/site-internet).
1 2 3 |
<Directory /var/www/*/> Options -Indexes </Directory> |
Ou si vous utilisez un hébergement mutualisé, vous pouvez spécifier cette règle Options dans le fichier .htaccess.
Interdire les fichiers .htaccess
Cette mesure n’a de sens que pour les administrateurs qui ont accès aux fichiers de configuration de leur serveur (hébergement dédié par exemple), et non pas les hébergements mutualisés.
Par défaut avec apache2 sur Debian, les .htaccess sont justement interdits comme je l’ai remarqué ici. Et c’est préférable, car les fichiers .htaccess peuvent être déposés par des attaquants, qui trouvent ainsi le moyen de manipuler totalement le serveur web. D’ailleurs en dernière partie de cet article, je me méfie des .htaccess que je ne connais pas !
Ainsi pour la sécurité il est préférable de conserver le réglage AllowOverride None sur votre VirtualHost apache.
Lorsque vous installez des extensions, prenez garde à regarder les fichiers .htaccess qu’elles proposent avec la commande find . -name '.htaccess' pour retranscrire la configuration dans votre fichier de configuration. Le non-respect de ce conseil peut mener à des trous de sécurité notamment avec les extensions qui proposent d’uploader des fichiers comme group-document.Par exemple avec akismet :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
<Directory /var/www/*/wp-content/plugins/akismet/> # Akismet # Only allow direct access to specific Web-available files. # Apache 2.2 <IfModule !mod_authz_core.c> Order Deny,Allow Deny from all </IfModule> # Apache 2.4 <IfModule mod_authz_core.c> Require all denied </IfModule> # Akismet CSS and JS <FilesMatch "^(form\.js|akismet\.js|akismet\.css)$"> <IfModule !mod_authz_core.c> Allow from all </IfModule> <IfModule mod_authz_core.c> Require all granted </IfModule> </FilesMatch> # Akismet images <FilesMatch "^logo-full-2x\.png$"> <IfModule !mod_authz_core.c> Allow from all </IfModule> <IfModule mod_authz_core.c> Require all granted </IfModule> </FilesMatch> </Directory> |
Cloisonner l’environnement d’exécution PHP
J’ai été très surpris de la configuration d’Apache par défaut qui autorise un script php d’un site X à aller lister le contenu d’un site Y, voire même le modifier. La fonction PHP scandir(../) fait des ravages. Voici comment cloisonner l’environnement d’exécution PHP, un point absolument vital pour ne pas qu’une faille sur un site soit pénalisante sur les autres sites.
1 2 3 4 5 |
<Directory /var/www/mon-site/> <IfModule mod_php5.c> php_admin_value open_basedir "/var/www/mon-site/" </IfModule> </Directory> |
Pour aller plus loin dans le cloisonnement, je préconise même l’exécution du moteur PHP dans un chroot grâce à php-fpm.
Désactiver les fonctions PHP dangereuses
Certaines fonction php sont tellement utilisées par les pirates, et pas tellement par les applications légitimes, qu’on peut les désactiver.
Dans le fichier php.ini situé dans /etc/php/apache2/
1 |
disable_functions = pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority, |
Malheureusement on ne pas ajouter eval ici, car il ne s’agit pas d’une fonction mais une construction de language. Pour ce faire, il faudra utiliser suhosin.
En partie 5.2 de cet article, j’évoque d’autres fonctions potentiellement dangereuses. Il faut préalablement vérifier que vos CMS ou vos applications n’en font pas usage.
Interdire l’accès au serveur par son adresse IP
Votre site internet accessible à http://www.mon-site.fr ne doit en aucun cas être accessible par votre IP http://123.123.123.123. Les pirates scannent quotidiennement l’internet, et montrer un site visible sur une adresse IP sans nom de domaine ouvre la porte aux attaques aveugles pour lesquelles votre IP est la seule raison d’être attaqué.
Donc assurez-vous de répondre un code 403 : forbidden sur votre IP. Vous pouvez y arriver par exemple avec les règles suivantes dans le virtualhost par défaut d’apache /etc/apache2/sites-enabled/000-default.conf. Le préfixe 000 permet de s’assurer que ce VirtualHost sera utilisé si la requête du client web ne réfère à aucun domaine configuré sur le serveur (précédence par ordre alphabétique).
1 2 3 4 |
DocumentRoot /var/www/html <Directory /var/www/html> Require all denied </Directory> |
Le mot-clé _default_ peut également être emplyé pour ce VirtualHost un peu particulier.
Si votre serveur est hébergé chez OVH avec un hostname de type ns123456.ip-123-123-123.eu , je vous recommande de ne pas configurer de certificat SSL en bonne et due forme sur ce domaine, car le certifical auto-signé « snakeoil » rendra l’accès encore plus compliqué via HTTPS avec les navigateurs et les outils CLI comme cURL et wget. Après tout, c’est ce qu’on veut, non ?
Précautions pour l’accès aux fichiers
La méthode de dépôt et d’accès aux fichiers doit être sécurisée elle aussi avec un mot de passe fort et un cryptage lors du transfert des données. Personnellement j’utilise ssh et scp sur mes serveurs dédiés, avec des protections configurées dans openssh.
La plupart des hébergements mutualisés proposent ftp, il vaut mieux basculer sur ftps (ftp + SSL) lorsque c’est possible. Les serveurs ssh proposent parfois sftp (ssh + ftp) qui est un bon choix, bien que pas aussi bon que scp (le pendant « transfert de fichiers » de ssh).
Comme bons clients Windows pour ftps on a FileZilla, et pour scp on a WinSCP.
Restrictions sur MySQL
Utilisateur dédié à la base de données
Une règle qui peut paraître évidente, c’est d’utiliser un utilisateur MySQL différent pour chaque base de données gérée. Comme cela, même si un site internet est compromis, les bases de données ne risquent pas d’être touchées.
Ces ordres MySQL ci-dessous créent un utilisateur my-user qui a les droits de lecture et de modification sur la table1 de la base de données my_db, et les droits de lecture seule sur la table2. Vous pouvez les rentrer en ligne de commande ou avec une interface utilisateur comme PhpMyAdmin.
1 2 3 |
mysql> CREATE USER 'my-user'@'localhost' IDENTIFIED BY 'my-password'; mysql> GRANT SELECT, INSERT, UPDATE, DELETE, ALTER, CREATE, DROP, INDEX ON `my_db`.`table1` TO 'my-user'@'localhost'; mysql> GRANT ON `my_db`.`table2` TO 'my-user'@'localhost'; |
Appliqué à WordPress on peut autoriser la modification sur toutes les tables une à une, sauf la table wp_users. En effet, vous ne voulez pas qu’un pirate crée un Administrateur supplémentaire qui aura le droit de vie et de spam sur le site.
Après vérification cela donne :
1 2 3 4 5 6 7 8 9 |
mysql> show grants for 'my-user'@'localhost'; +----------------------------- | Grants for my-user@localhost +----------------------------- | GRANT USAGE ON *.* TO 'my-user'@'localhost' IDENTIFIED BY PASSWORD '*01234[...]' | GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, INDEX, ALTER ON `my_db`.`wp_options` TO 'my-user'@'localhost' | GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, INDEX, ALTER ON `my_db`.`wp_comments` TO 'my-user'@'localhost' | GRANT SELECT ON `my_db`.`nn_users` TO 'my-user'@'localhost' [...] |
Si le site internet n’a pas vocation à être modifié, l’idéal serait de basculer toutes les tables en lecture seule.
Pour certains sites à page unique, on peut même se demander s’il ne serait pas préférable d’aspirer cette page sous la forme d’un fichier html et de quelques css et js, afin de transformer le CMS vulnérable en page statique invulnérable.
Modifier les préfixes de table
Les préfixes de table sont par défaut wp_ mais dès l’installation, WordPress nous propose de les modifier. Ces préfixes permettent d’éviter qu’un attaquant ne puisse deviner trop facilement le nom des tables.
Ces préfixes ont également un aspect pratique puisqu’ils permettraient en théorie de mettre plusieurs sites internet dans une même base de données, bien que cela ne soit pas la chose la plus propre du monde.
Pour un site internet existant, on peut modifier les préfixes dans la base MySQL mais il faut modifier le paramètre table_prefix en conséquence dans le fichier wp-config.php. Également dans la base elle-même il y a des champs dans la table wp_options qui doivent être modifiés.
Restrictions propres à WordPress
Règles de bon sens
Pour commencer, les comptes utilisateurs sur WordPress doivent être difficiles à deviner et le mot de passe doit être « fort » c’est à dire long et avec différents types de caractères : des lettres minuscules et majuscules, des chiffres et des signes de ponctuation.
Les extensions peuvent présenter des failles de sécurité donc il vaut mieux les limiter au strict minimum. Ne jamais installer de thème ou d’extension trouvée n’importe où sur le web car les virus sont courants.
Mises à jour de WordPress
WordPress est particulièrement visé par les pirates par sa forte présence sur le web, et de plus c’est lui qui est en première ligne sur votre serveur. Pour ces raisons il est absolument indispensable d’effectuer des mises à jour régulières.
Les mises à jour de sécurité sont généralement faites de manière autonomes par le CMS, et il existe des extensions pour automatiser cette tâche. On peut bien sur envisager de mettre en service un script qui va télécharger la nouvelle version de WordPress pour la décompresser à la racine du site, cette simple opération devrait normalement suffire.
Enfin, les mises à jour concernent également les extensions et les thèmes qui peuvent présenter des failles de sécurité.
Restriction des pages de login et d’administration aux utilisateurs amis
La page /wp-login.php et le dossier /wp-admin contiennent des formulaires de connexion. Pour limiter les risques il vaut mieux restreindre l’accès à des adresses IP « amies » ou grâce à une protection par mot de passe via le serveur web.
1 2 3 4 5 6 |
<Directory /var/www/*/wp-admin/> Include /etc/apache2/ip.protection.conf </Directory> <Files "wp-login.php"> Include /etc/apache2/ip.protection.conf </Files> |
Dans cet exemple précis, j’ai déplacé les règles de restriction IP dans un fichier commun que je peux modifier une fois pour toutes. Voici le contenu de ip.protection.conf .
1 2 3 4 5 |
Order Deny,Allow Deny from all Allow from 192.168.1.0/24 Allow from 1.2.3.4 |
Une variante assez pratique est d’autoriser ces contenus aux IP amies, mais aussi aux autres IP qui réussiraient à saisir un mot de passe correct. Exemple :
1 2 3 4 5 |
<Directory /var/www/*/wp-admin/> Include /etc/apache2/ip.protection.conf Include /etc/apache2/md5.protection.conf Satisfy any </Directory> |
Le contenu de md5.protection.conf sera :
1 2 3 4 |
AuthUserFile /etc/apache2/htdigest AuthType Digest AuthName "Mon-domaine" Require valid-user |
Le contenu de htdigest sera créé grâce à la commande htdigest (et non pas htpasswd). Exemple :
1 2 3 4 5 6 7 |
root@ordinateur:/etc/apache2# htdigest -c ./htdigest Mon-domaine Mon-utilisateur Adding password for Mon-utilisateur in realm Mon-domaine. New password: Re-type new password: root@ordinateur:/etc/apache2# cat ./htdigest Mon-utilisateur:Mon-domaine:674c75d133e0f32dd638b69569e089f1 |
Attention, on fait appel ici au module auth_digest qui doit être activé dans /etc/apache2/mods-enabled . Selon la RFC2617, le « realm » utilisé devrait être unique pour chaque utilisateur.
Pour moi, auth_digest est largement supérieur à auth_basic (ligne AuthType Basic ), car même sans l’utilisation de HTTPS, le mot de passe n’est jamais transmis en clair sur le réseau. Ainsi, je ne vois pas quel peut être l’avantage de auth_basic sur auth_digest. Ah si, j’en ai trouvé un sur Wikipédia : la fonction de hachage MD5 est interdite sur les applications qui doivent être certifiées FIPS.
Dans les deux cas, le mot de passe n’est pas stocké en clair sur le serveur (commande htdigest pour auth_digest et htpasswd pour auth_basic).
Interdictions totales sur certains fichiers
Le fichier de configuration wp-config.php est utilisé par les autres pages php du site, mais il ne doit jamais être lu par un internaute.
1 2 3 |
<Files wp-config.php> Require all denied </Files> |
Les fichiers .htaccess doivent subir le même traitement afin de ne pas dévoiler sa configuration à n’importe qui. En général cela est configuré par défaut sur le serveur web, mais il est toujours bon de vérifier.
1 2 3 |
<Files .htaccess> Require all denied </Files> |
La page /xmlrpc.php est à l’origine destinée à recevoir des commandes distantes pour manipuler le contenu du site WordPress. En pratique cette page reçoit très souvent des attaques par force brute, les attaquants cherchent à deviner le mot de passe. A mon avis cette page doit recevoir le même traitement que wp-login.php ou même wp-config.php .
Interdiction d’exécuter du php dans le répertoire uploads
Le répertoire /wp-content/uploads contient tous les media uploadés sur le site (images, sons, vidéos) dans une arborescence souvent triée par date. J’ai souvent vu des attaques qui déposent dans ce répertoire un script php malicieux plutôt qu’un media, et elles n’ont ensuite plus qu’à l’exécuter.
En interdisant l’exécution de php dans ce dossier, on s’assure que l’attaquant ne pourra pas exécuter son code malicieux même s’il réussit à le déposer. Voici ce qu’on peut faire dans le fichier de configuration apache.
1 2 3 4 5 6 7 8 9 10 |
<DirectoryMatch "wp-content/uploads/"> <FilesMatch ".+\.ph(p[345]?|t|tml)$"> Require all denied </FilesMatch> <IfModule mod_php5.c> php_flag engine off </IfModule> SetHandler default-handler Options -ExecCGI </DirectoryMatch> |
Il y a deux points bien distincts dans cette configuration : le php_flag désactive complètement l’exécution de php dans ce répertoire, et le FilesMatch en interdit l’accès. Si on se contente du php_flag , le fichier php reste téléchargeable comme du plain text. Si on se contente du FilesMatch , un code php dont l’extension n’est pas php pourrait être exécuté. Avec ces deux précautions c’est plutôt solide.
Interdiction d’exécuter du php sauf là où c’est interdit
Une variante de la proposition ci-dessus, c’est d’interdire l’exécution de tout script php, sauf ceux qui se nomment index.php. Cette règle n’empêchera pas les includes de fichiers php, mais refusera l’accès direct à un script au nom bizarre. Cette variante est encore plus sécuritaire, car elle n’autorise que les fichiers explicitement nommés. C’est une méthodologie que l’on retrouve communément dans les pare-feu iptables qui commencent par interdire tout trafic (POLICY) pour ensuite autoriser certains protocoles (ACCEPT).
La configuration est détaillée dans cet article, qui met l’accent sur un bug de configuration apache dans lequel il ne faut pas tomber.
Désactiver la modification des fichiers php depuis l’interface wp-admin
Dans WordPress il existe une option pour désactiver la modification des fichiers php depuis l’interface d’administration. Les utilisateurs ou les administrateurs du site ne pourront pas modifier les scripts php.
Dans wp-config.php :
1 |
define('DISALLOW_FILE_EDIT', true); |
Masquer la version de WordPress
Dans l’idée, il faut masquer votre version de WordPress, car c’est une information que les pirates peuvent utiliser pour choisir leurs attaques. La plupart du temps, les pirates vont sur des sites où l’on peut télécharger des exploits. Un exploit est un code informatique minimaliste qui permet d’exploiter une faille. Et bien souvent, un exploit vise une version en particulier.
Pour masquer la version de WordPress on peut utiliser un module comme Acunetix WP Security ou encore modifier les fichiers de son thème. Si on veut aller au bout des choses, masquer le numéro de version n’est pas si simple, comme cela est détaillé sur cet article de boiteaweb.fr. De là à affirmer que cette précaution est inutile, il n’y a qu’un pas… que je ne franchirai pas.
Supprimer l’utilisateur ID 1
Par défaut le premier utilisateur (administrateur) porte l’ID 1 dans la table wp_users. C’est une vérité générale pour tous les WordPress, et c’est une information qui peut être utile aux attaquants pour viser les droits d’administration en modifiant le mot de passe de l’utilisateur 1.
Dans MySQL, vous pouvez donc modifier l’ID de l’administrateur pour une autre valeur.
Extensions WordPress pour la sécurité
Il existe des extensions dont le seul but est d’améliorer la sécurité de WordPress. Les administrateurs les plus scrupuleux préféreront s’occuper de la sécurité à la main. Sinon il existe par exemple Acunetix WP Security qui semble plutôt sérieux. Le principal avantage de cette extension est de voir la TODO list des actions à réaliser.
Restreindre les droits des fichiers
Avec des constats simples, on peut retirer beaucoup de libertés qui sont trop souvent exploitées par les pirates. En effet, combien de fois dans l’année avez-vous besoin de modifier les fichier php qui constituent wordpress, en dehors des mises à jour ? Personnellement pas très souvent et c’est pourquoi j’interdis toute modification de ces fichiers, et je débloque cette mesure avant chaque mise à jour.
Les droits par défaut
La communauté WordPress conseille d’appliquer par défaut les droits suivants sur toute l’installation. Du chmod 755 sur les dossiers et 644 sur les fichiers.
1 2 3 |
root@ordinateur:/var/www/mon-site# find . -type d -exec chmod 755 {} \; root@ordinateur:/var/www/mon-site# find . -type f -exec chmod 644 {} \; root@ordinateur:/var/www/mon-site# chown www-data:www-data -R . |
De mon coté je suis beaucoup plus restrictif que cela.
Les restrictions sur les fichiers wordpress
Chacun doit élaborer un script selon ses besoins qui restreint pas à pas les droits des fichiers. Petite anecdote amusante, la restriction des droits des fichiers pour ne laisser que la lecture seule est de nature a accélérer le site internet, selon quelques tests que j’ai faits.
On définit l’utilisateur des fichiers et le groupe, et on interdit tous les autres.
1 |
root@ordinateur:/var/www/mon-site# chown -R www-data:www-data . |
Pour refuser de créer des nouveaux fichiers et dossiers à la racine du site.
1 |
root@ordinateur:/var/www/mon-site# chmod 550 . |
Restriction lecture et exécution pour les répertoires, sachant que l’exécution pour un répertoire signifie « traverser ». Lecture seule pour tous les fichiers.
1 2 |
root@ordinateur:/var/www/mon-site# find ./ -type d -exec chmod 550 {} \; root@ordinateur:/var/www/mon-site# find ./ -type f -exec chmod 440 {} \; |
On doit ouvrir en écriture uniquement dans le dossier d’uploads.
1 2 3 |
root@ordinateur:/var/www/mon-site# find wp-content/uploads -type d -exec chmod 770 {} \; root@ordinateur:/var/www/mon-site# find wp-content/uploads -type f -exec chmod 660 {} \; root@ordinateur:/var/www/mon-site# chmod 550 wp-content/uploads |
Pour insister encore sur la non-modification des scripts php existants, j’utilise l’ordre chattr pour manipuler des attributs de fichiers disponibles sous Linux, et tout particulièrement le bit d’inamovibilité. Cela vient se superposer aux droits des fichiers standards, de sorte que même root ne peut pas modifier un fichier avec le bit d’inamovibilité qui est positionné. Cette protection est probablement une des plus efficaces de cette page.
1 |
root@ordinateur:/var/www/mon-site# find ./ -name "*.php" -exec chattr +i {} \; |
Protection du .htaccess
1 2 3 |
root@ordinateur:/var/www/mon-site# find . -name ".htaccess" -exec chown root:root {} \; root@ordinateur:/var/www/mon-site# find . -name ".htaccess" -exec chmod 440 {} \; root@ordinateur:/var/www/mon-site# find . -name ".htaccess" -exec chattr +i {} \; |
Parfois on trouve des archives ou des fichiers qui auraient du être supprimés. Il vaut mieux en interdire l’accès.
1 |
root@ordinateur:/var/www/mon-site# chmod 000 *.zip *.tgz readme.html license.txt |
Attention si vous mettez en place toutes ces restrictions par un script, il vous faudra développer le script inverse pour tout débloquer et permettre les mises à jour !
Si jamais cela ne suffisait pas
Les garde-fous pour limiter la casse SEO
Imaginez une seconde que toutes ces mesures n’aient pas suffi pour empêcher un pirate de déposer du spam sur votre site internet, est-ce que vous voulez vraiment autoriser les moteurs de recherche à lire des URL qui contiennent des mots magiques comme des noms de médicaments pour l’impuissance ? Probablement pas.
Voici ce que je vous propose dans le VirtualHost du site internet. Avec cette règle, toute URL qui contient des mots interdits est redirigée vers la page d’accueil du site internet avec un code « 301 : Moved permanently ».
1 2 3 4 5 6 |
RedirectMatch 301 ^.*pharmacy.*$ / RedirectMatch 301 ^.*cialis.*$ / RedirectMatch 301 ^.*viagra.*$ / RedirectMatch 301 ^.*propecia.*$ / RedirectMatch 301 ^.*sildenafil.*$ / [...] |
Ou encore, une variante qui rend le code « 410 Gone ». Ce code d’erreur est supposé indiqué au visiteur que la page a été supprimée définitivement et qu’il faut supprimer cette URL de tout lien et de tout marque page. L’inconvénient du code 410 configuré par Apache, c’est que la page affichée est très moche.
1 2 3 4 5 6 |
RedirectMatch 410 ^.*pharmacy.*$ RedirectMatch 410 ^.*cialis.*$ RedirectMatch 410 ^.*viagra.*$ RedirectMatch 410 ^.*propecia.*$ RedirectMatch 410 ^.*sildenafil.*$ [...] |
Comme amélioration, on pourrait envisager une réécriture d’URL qui pointe vers une page php qui rende à la fois un code 410 dans le header HTTP, mais qui finisse tout de même pas rediriger l’internaute (avec javascript) vers une page plus sympathique.
La règle de redirection d’URL pour rediriger toute URL qui contient le répertoire « dossier-involontaire » :
1 2 |
RewriteCond %{REQUEST_URI} ^/dossier-involontaire/ RewriteRule ^.*$ /inexistant.php? [L] |
Et dans la page inexistant.php on met par exemple :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
<?php header("HTTP/1.1 410 Gone"); ?> <!DOCTYPE html> <html> <head> <meta name="robots" content="noindex,nofollow" /> <meta name="language" content="fr-FR" /> <meta charset="utf-8" /> <title>Page inexistante</title> <script> setTimeout(function() { window.location.href = "/"; }, 100); </script> </head> <body> <p>Cette page n'existe pas, merci de la supprimer de vos marques-pages et de continuer sur <a href="/">le site internet</a> </body> </html> |
Cela aura pour effet de répondre à l’URL non désirée en indiquant un code 410. Aucun moteur de recherche ne devrait jamais indexer cette page, et si cela ne suffisait pas la balise meta robots y veille. Si jamais un internaute se promène sur cette page interdite, le script javascript va se charger de le rediriger en 100 ms.
Ce garde-fou sur des URL manifestement involontaires est presque une technique SEO, car elle peut éviter un drame de référencement si jamais le site est massivement spammé.
Les pages qui sentent le piratage : points à surveiller
Les piratages de sites internet sont inventifs et suivent de près les mises à jours des CMS. Néanmoins les scripts malicieux comportent toujours des points communs :
- Soit ces scripts malicieux se font passer pour ce qu’ils ne sont pas avec une indentation propre et des commentaires du style « don’t be afraid, this is normal, … »
- Soit ces scripts malicieux sont « minifiés » c’est à dire illisibles, avec toutes les ordres sur la même ligne et même éventuellement encodé en base64.
Quoi qu’il en soit pour partir en chasse contre ces scripts malicieux, il faut particulièrement surveiller les fonctions php eval() , base64_decode() , ou encore assert() .
Exemple de recherche :
1 2 |
root@ordinateur:/var/www/mon-site/wp-content/uploads# grep -r 'eval(' . index376a9.php:$eNbEZNvITAUwiKl=""."".""."b"."".""."a".""."".chr(115).""."".""."e".""."".chr(54)."".""."".chr(52).""."".""."_"."".""."".""."".chr(100)."".""."".""."".chr(101).""."c".""."o"."".""."d"."".""."".chr(101);$fioyIMKYMNrIAf=$eNbEZNvITAUwiKl(""."X"."".""."".""."1".""."B".""."".chr(80).""."".chr(85).""."1".""."".""."".""."Q".""."".""."=");$CcpiW [...] |
Ce fichier bizarrement nommé et dont le contenu est bizarre lui aussi est malicieux, cela ne fait aucun doute. Pour dénicher des perles de ce type, voici des exemples de commandes à utiliser.
1 2 3 |
root@ordinateur:/var/www/mon-site# find . -name "*.php*" -exec grep 'eval(' {} \; root@ordinateur:/var/www/mon-site# find . -name "*.php*" -exec grep 'assert(' {} \; root@ordinateur:/var/www/mon-site# find . -name "*.php*" -exec grep 'base64_decode(' {} \; |
Attention tout ce qui sort n’est pas nécessairement malicieux, disons que cela peut vous aider à vous mettre sur la piste.
A surveiller également : le contenu des fichiers .htaccess ainsi que leur emplacement. Les pirates en déposent parfois pour maîtriser la configuration de votre serveur web.
1 |
root@ordinateur:/var/www/mon-site# find . -name ".htaccess" |
Enfin, nous l’avons déjà évoqué, tout script php dans le répertoire uploads est suspect (sauf index.php vide).
1 |
root@ordinateur:/var/www/mon-site/wp-content/uploads# find . -name "*.php*" |
Conclusion
Les attaquants ont toujours un temps d’avance sur les attaqués, cela est ainsi. La mise en œuvre de toutes ces protections devrait vous rendre nettement moins vulnérable, mais une attaque réussie est toujours possible. Si vous avez des techniques personnelles et des améliorations à suggérer, n’hésitez pas à réagir !
Si la longueur de cet article vous a découragé, ou si vous ne pensez pas pouvoir mener ces actions à bien, nous proposons nos services d’hébergement.
Article intéressant et assez complet.
Néanmoins, je ne suis pas d’accord sur la partie authentification.
Digest prévient surtout d’une attaque mitm, ce qui est assez rare, et peut-être facilement évitée par l’utilisation de tls.
En revanche, avec digest les mots de passes doivent être sotckés en clair sur le serveur, ce qui me semble beaucoup plus préjudiciable.
En ce qui concerne les restrictions dans le répertoire d’upload, penses à également interdire l’utilisation des fichiers .htaccess (AllowOverride None).
Merci pour ta contribution.
Oui en effet, avec l’utilisation de https, le gros inconvénient de auth_basic disparaît : le mot de passe n’est plus transmis en clair sur le réseau et les mitma évitées. J’ajoute la précision dans l’article. Selon cet article, auth_digest sans https semble malgré tout vulnérable aux attaques mitm également.
Attention : le fichier de mot de passe n’est pas stocké en clair sur le serveur ni avec htpasswd, ni avec htdigest. Ces fichiers à plat sont tout de même, et de la même manière que le fichier shadow, sensibles à une attaque par force brute s’ils venaient à être dérobés.
L’interdiction des fichiers .htaccess m’avait échappée, je rajoute.
Effectivement, ma réponse manque de précision.
Digest protège de l’écoute passive, il est clair que si l’attaquant forge des réponses, même tls sans hsts et hpkp ne peut éviter le vol du mot de passe.
Pour digest, c’est bien le hash qui est stocké, mais à la différence de plain, c’est le hash qui est utilisé pour l’authentification, et non le mot de passe.
C’est toujours un plaisir d’échanger sur des sujets techniques. De mon coté j’avoue ne pas être familiarisé avec HSTS et HPKP, voilà de la lecture de chevet qui promet d’être intéressante 🙂