Wordpress (et autres) mesure préventive - mode PHP (FPM/Fastcgi ?)

Bonjour à tous,

Au Retzien on a une net recrudescence du piratage chez nos hébergés. (serveur de site web mutualisés sous Debian/ISPconfig)

J’ai mis en place (en test) des mesures curatives : Utilisez vous un anti-malware / un scanner de code PHP? mais forcé de constater que ça revient vite, doit y avoir des trous dans la raquette…

Nous utilise le mode php « php-fpm ». Qui permet un bon cloisonnement. Le code PHP est lancé avec un utilisateur restreint par virtualhost (et plus www-data pour tout le monde comme il fût un temps) ce qui évite une contagion sur d’autres hébergés quand un hacker arrive à exécuter du code malveillant. Mais ce mode à (a mon avis) un inconvénient majeur c’est qu’on a plus besoin de faire de chmod pour permettre l’écriture dans des répertoires. Vu que l’utilisateur qui exécute PHP est le même que celui avec lequel on dépose nos fichiers sur le serveur (par FTPS/SFTP/SSH). Pourtant je ne cesse de lire les louanges de ce mode php-fpm sur le net.

J’ai l’impression que le mode PHP Fast-CGI permettrai de réduire la surface d’attaque parce que PHP en Fast-CGI a besoin qu’on modifie les permissions de fichier (chmod) pour écrire. Mais de ce que je lis c’est pas l’idéal en production :sweat_smile: du coup je suis un peu perdu… si vous avez des lumières à apporter je suis content.

(j’ai observé beaucoup de modification du index.php du wp-config.php de fichier dans wp-include… Des trucs qui normalement ne sont pas accessibles en écritures, d’où ma question… a noter que ces fichier, une fois décontaminé sont re-contaminé très peu de temps après (dans les 24h) alors qu’il n’y a pas de tâche cron, que le wordpress est à jour, peu de plugin…)

Les mesures déjà prisent :

Si vous avez d’autres idées à soumettrez sur d’autres mesures préventive je suis preneur !

(par exemple, j’hésite à généraliser sur les wordpress le changement d’emplacement WP_CONTENT) :

define( ‹ WP_CONTENT_DIR ›, ‹ /var/www/blabla.fr/web/inside › );
define( ‹ WP_CONTENT_URL ›, ‹ https://www.blabla.fr/inside › );

Bon là je focalise sur Wordpress parce que c’est ce qui est le plus courant sur nos serveurs - et le plus courant sur le web, donc le plus attaqué… - mais y’a aussi du spip et autres qui sont aussi touchés…

David

Bonjour David. Une question pour mon info perso (qui ne résoudra pas ton problème dans l’immédiat) : quelle est la politique SELinux de tes systèmes ?

Aucune, SELinux n’est pas compatible avec ispconfig

a noter que ces fichier, une fois décontaminé sont re-contaminé très peu de temps après

Dans ma petite expérience :

  • ta base de données est infectée
  • le cache est infecté
  • les utilisateurs (admin, éditeurs, intégrateurs web, toi) poussent du contenu vérolé depuis leur poste de travail
  • la charge virale est cachée dans des images, des fonts, etc
  • une faille PHP est exploitée
  • le panel est infecté, le système est infecté (et ça c’est compliqué à prouver et corriger)

Je ne crois pas qu’on puisse efficacement sécuriser WordPress. Le logiciel, par nature, requière de se modifier lui-même, pour les mises à jour, la configuration et la publication de contenu décidé par un usager.

Cependant, on peut limiter la casse. Certaines que tu as déjà mises en place. Attention, certains considèrent que mon mode d’action est trop extrême, voire usine à gaz :smiley:, et puis ce n’est que mon expérience personnelle.

An niveau système

  • au moins un utilisateur système par déploiement
  • des utilisateurs nominatifs pour les déploiements, qui ont le droit d’écrire au nom de l’utilisateur système qui fait tourner PHP FPM (en utilisant un mécanisme tel que sudo à travers SSH)
  • NFS/bind mount: faire en sorte que l’utilisateur PHP-FPM accède au répertoire de WP en readonly
  • un mail relay (avec antivirus et possibilité de séquestrer le trafic sortant avec un bouton) pour les courriers sortant
  • un proxy web en sortie pour monitorer et filtrer ce que WP fait
  • un cron qui corrige les permissions toutes les minutes
web_public=/var/www/ # répertoire arbitraire pour illustrer
find $web_public -type f -exec chmod 0600 '{}' +
find $web_public -type d -exec chmod 0700 '{}' +

# on peut aussi imaginer chercher et trouver les fichiers qui
# appartiennent à on ne sait qui et faire échouer le cron

Au niveau de PHP FPM

  • exclure les fonctions dangereuses
  • choucrouter
  • configurer le système de mail à utiliser

Au niveau du serveur web

  • CSP (et interdire tout le JS inline si possible)
  • HSTS
  • HTTPS only
  • vérifier avec un service comme SecurityHeaders.com

Au niveau de WordPress

  • bouger /wp-admin plugin wp-admin login
  • WordFence, payant, pas libre, si d’autres ont des équivalents ça peut être intéressant (attention à bien vérifier que $_SERVER['REMOTE_ADDR'] est correctement configuré par le ProxyPass de ton Apache)
  • 2FA pour les admins et éditeurs
  • gérer les utilisateurs (les suspendre après un temps d’inactivité)
  • configurer un mail relay qui scan les courriels sortant

Il y a une difficulté que je n’ai pas réussi à résoudre dans la boîte où j’avais pour mission de sécuriser les WP qui étaient piratés tous les 4 matins : les mises à jour automatiques. Autant laisser WP se mettre à jour lui-même est une faille de conception, autant la sécurité de l’écosystème repose sur cette fonctionnalité pour assurer la sécurité… Bref, une solution est d’avoir un cron qui utilise un utilisateur système différent et qui lance wp-cli pour effectuer les mises à jour.

Ça marchottait quand je l’ai mis en place, beaucoup de plaintes des utilisateurs (intégrateurs web) qui voulaient installer des backdoors plugins comme ils voulaient.

En conclusion

Bien sûr, tout faire d’un coup est assez compliqué, faut faire la sécurisation graduellement, du plus simple au plus complexe.

On peut aller plus loin. Personnellement, j’utilise des conteneurs pour isoler les environnements. Il y a des pour et des contre, le remède pourrait être pire que le mal. Mais c’est à envisager si choucrouter PHP FPM n’est pas suffisant.

1 « J'aime »

Ha ouai, j’ai jamais vu d’attaque de ce genre. Comment tu détecte/purge ça ?

ça j’ai le sentiment que ça s’étalerait plus si c’est le cas… là il y a ~5 sites bien vérolés, 10 qui ont des trucs étrange…
Sur les ~200 sites hébergés sur cette machine c’est à la fois beaucoup et pas tant…

Ha oui ça c’est radical, mais effectivement l’utilisateur ne peut ensuite même plus upload une image…

Tu as une liste de ce que tu as bloqué et qui n’entrave pas (trop) le fonctionnement des CMS ?

Je suis en A pour CSP, HSTS sur SecurityHeaders.com

C’est exactement ce que j’ai fais avec : https://framagit.org/kepon/wp-cli-isp

Merci pour ton aide LupusMichaelis

1 « J'aime »

Comment qu’on purge une base infectée: c’est compliqué. Je n’ai pas de méthode satisfaisante. Quand ça arrivait, je dégageais tout ce qui est cache (les transients et compagnie), puis je parcourais à l’œil un dump de la base. Genre repérer des choses qui paraissent louches, mais ce n’est pas très satisfaisant comme approche.

Ha oui ça c’est radical, mais effectivement l’utilisateur ne peut ensuite même plus upload une image…

Ça dépend comment tu le fais. Par exemple, tu montes un répertoire en readonly pour qu’il soit la racine de ton web, puis tu monte un répertoire dans /uploads en readwrite. Tu peux faire pareil avec les différents répertoires de cache que WP aime jeter un peu partout.

Tu as une liste de ce que tu as bloqué et qui n’entrave pas (trop) le fonctionnement des CMS ?

Je ne me souviens plus et je ne trouve pas dans mes notes de ce que j’avais bloqué à l’époque. Bon, on trouve des choses en cherchant rapidement :

C’est vrai que 5/200 (si ce sont tous des WP), c’est pas mal. Faudrait voir quels sont les plugins en commun !

1 « J'aime »

J’ai du mal à voir en quoi PHP Fast-CGI serait plus sécure que PHP-FPM. Pour moi, définir un utilisateur système distinct pour chaque site (et distinct de www-data) est ce qui assure la meilleure isolation.

Par contre je rejoins @LupusMichaelis et ce que tu dis également @kepon, la plus grosse faille de sécurité est que wordpress exige de pouvoir se modifier lui même. Si tu as levé ce problème, alors tu as beaucoup plus de marges de manoeuvres.

En l’occurrence, en alternative de la technique bind-mount, je recommanderais 2 techniques indépendantes pour renforcer les droits.

Technique 1 - Définir un utilisateur spécifique pour la MAJ

Donc utiliser non pas 2 mais 3 utilisateurs, c’est à dire :

  • 1 utilisateur pour le serveur HTTP
  • 1 utilisateur pour PHP-FPM (pour chaque site)
  • 1 utilisateur pour la mise à jour (unique ou pour chaque site)

Auquel cas, pour définir les droits tu peux soit :

  • En conservant les droits POSIX classiques, owner les fichiers avec l’utilisateur de mise à jour, et fixer les droits sur les fichiers à rwXr-Xr-X (i.e. dossiers à rwxr-xr-x et fichiers à rw-r–r–) pour que l’utilisateur PHP-FPM n’ait pas accès en écriture.
  • En utilisant les droits étendus, définir des ACLs (setfacl) pour fixer des droits spécifiques pour chaque utilisateur. Il faut indiquer le support des ACLs lors du montage du système de fichiers. C’est un peu compliqué au départ mais une fois qu’on a compris le mécanisme, on peut facilement définir les droits pour les fichiers actuels ainsi que les droits par défaut pour les fichiers futurs.

Technique 2 - Définir les fichiers comme immutable

Si tu sais contrôler le moment où tu as besoin que tes fichiers soient modifiables, tu peux leur fixer le bit immutable le reste du temps.

C’est très simple, tu utilises chattr +i sur le fichier ou le dossier, il ne sera plus modifiable, ni le contenu, ni ses permissions. Seul un utilisateur avec la capability CAP_LINUX_IMMUTABLE (donc en gros l’utilisateur root) pourra retirer le bit i.

Donc dans ton script de MAJ, il suffit de retirer le bit i avant la MAJ et de le remettre après la MAJ, et tu bloques toute modification de tes fichiers pour l’immense majorité du temps.

Je n’ai jamais utilisé cette fonction en environnement web mais je ne vois pas pourquoi ça poserait problème.

Côté Gozdata (log.bzh)

On a la chance d’avoir beaucoup moins de problèmes avec nos wordpress que toi (c’est peut être le volume qui veut ça), mais si j’avais à renforcer la sécu je pense que je commencerais par une de ces deux techniques.

Ensuite, il faut garder à l’esprit quand on utilise nginx que l’on n’interprète pas les .htaccess, donc il faut bien penser à retranscrire les directives dans la conf du site nginx, mais je ne doute pas que tu l’aies déjà fait.

1 « J'aime »

PHP FPM = PHP FastCGI Process Manager = PHP FastCGI

FastCGI est simplement une évolution de CGI où au lieu de fork un processus par requête, on transmet les requêtes à un processus existant pour obtenir de meilleures performances.

:face_with_open_eyes_and_hand_over_mouth:

Et bien c’est ça d’utiliser des Panel sans avoir tout fait avec ces petites mains :upside_down_face:

C’est comme ça que c’est indiqué dans le panel :

Dans la config apache c’est implémenté différemment :

FastCGI

SetHandler "proxy:unix:/var/lib/php8.2-fpm/web53.sock|fcgi://localhost"

PHP-FPM

FCGIWrapper /var/www/php-fcgi-scripts/webXXX/.php-fcgi-starter .php

Et effectivement le mode FastCGI (que je n’utilise) est lancé avec l’utilisateur apache et non avec un utilisateur dédier contrairement au mode php-fpm donc je vais rester sur ce dernier… fausse piste !

1 « J'aime »

ça c’est une bonne idée intégrable dans mon environnement Je vais tenter un « tools » de « lock/unlock » des fichiers php pour les utilisateurs Wordpress

Merci