Bloquer les attaques sur son serveur Web (Apache + PHP) avec CrowdSec
Sommaire
I. Présentation
Dans ce tutoriel, nous allons voir comment l'outil gratuit CrowdSec va nous permettre de protéger un serveur Web (Apache et PHP) contre les attaques informatiques.
Pour rappel, CrowdSec est un outil open source, gratuit, français, qui s'inspire du célèbre outil Fail2ban et qui a pour objectif de protéger votre serveur, en détectant puis en bloquant les attaques.
Récemment, les équipes de CrowdSec ont travaillé sur le développement d'un bouncer PHP pour protéger les serveurs Web qui tournent sur "Apache + PHP". Une excellente une idée puisque PHP est utilisé sur une très très grande majorité des sites Internet à l'échelle mondiale.
A. L'objectif du jour
Pour suivre ce tutoriel, nous allons utiliser deux machines : un serveur Web qui sera notre cible, et une seconde machine pour émettre une attaque très légère, mais réelle. Ce qui donne :
- Un serveur Web sous Debian 11
- Apache et PHP installés, avec un site Web tout simple
- CrowdSec va être déployé pour protéger le serveur
- Un poste client pour simuler une attaque, sous Kali Linux pour ma part
- L'outil Nikto sera utilisé pour effectuer un scan du serveur Web afin de rechercher des vulnérabilités ou des erreurs de configuration
Avant d'aller plus loin, je vous invite à prendre connaissance de mon premier article au sujet de CrowdSec où je présente plus précisément l'outil et dans lequel nous avons vu comment protéger un serveur Web sous Nginx.
Si vous êtes prêt à découvrir ce second cas d'usage de CrowdSec, poursuivez la lecture !
II. Mise en place d'Apache et PHP sur Debian 11
Pour la mise en place du serveur Apache avec PHP, je vous recommande de lire ce tutoriel sur la mise en place d'un serveur LAMP sous Debian 11. Il servira de point de départ afin d'avoir un serveur Web opérationnel.
On va se contenter d'une simple page d'accueil "index.php" avec un message "Hello !". Pour créer cette page, suivez ces quelques étapes.
sudo nano /var/www/html/index.php
Insérez ce petit bout de code :
<?php echo "<h1>Hello !</h1>"; ?>
Enfin, supprimez la page d'accueil d'origine d'Apache.
sudo rm /var/www/html/index.html
Le serveur Web est prêt, nous avons une page PHP en place. Elle est accessible sur le domaine it-connect.tech.
III. Installation de CrowdSec sur Debian 11
Sur Debian 11, CrowdSec est directement dans les dépôts, ce qui va nous faciliter la vie. Il suffit de mettre à jour le cache des paquets et de lancer l'installation :
sudo apt-get update sudo apt-get install -y crowdsec
Le cas échéant, si le paquet n'est pas disponible, vous devez utiliser cette méthode pour réaliser l'installation :
curl -s https://packagecloud.io/install/repositories/crowdsec/crowdsec/script.deb.sh | sudo bash sudo apt install crowdsec
Lors de l'installation, CrowdSec va analyser votre machine afin de détecter les services présents et de télécharger les collections associées. Ces composants vont permettre à CrowdSec de détecter les attaques (sans les bloquer).
On peut lister les collections avec la commande suivante :
cscli collections list
Si la collection "base-http-scenarios" est présente dans la liste, ce qui normalement le cas si vous avez déjà installé Apache sur votre serveur, cela va notamment permettre de bloquer les mauvais User Agents, comme ceux utilisés par certains outils de scans. Ceci n'est qu'un exemple, car cette collection va détecter d'autres événements comme la recherche de backdoors, etc.
On peut regarder si nous avons des décisions actives au niveau de notre instance CrowdSec. En toute logique, non. Vérifions que ce soit bien le cas avec la commande ci-dessous issue de "cscli", l'ensemble de commandes associées à CrowdSec.
sudo cscli decisions list
IV. Mon serveur Web Apache + PHP vs Nikto
A. Premier scan avec Nikto
Rappel : Nikto est un outil open source qui sert à analyser un serveur Web à la recherche de vulnérabilités ou de défaut de configuration.
Avant toute chose, il faut installer l'outil Nikto sur la machine qui sert à réaliser le scan. Pour ma part, j'utilise Kali Linux via WSL (Windows Subsystem for Linux).
L'installation s'effectue très simplement :
sudo apt-get update sudo apt-get install nikto
Avant d'exécuter le scan Nikto, vous pouvez vérifier que votre machine Kali Linux parvient à charger la page d'accueil de votre site :
curl -I it-connect.tech
Si vous obtenez un résultat avec un code de retour HTTP égal à 200, c'est tout bon ! Maintenant, on va lancer un scan de notre serveur Web avec Nikto. Pour cela, on spécifie l'adresse IP de l'hôte cible ou le nom de domaine, et on laisse tourner. Comme ceci :
nikto -h it-connect.tech
Suite au scan avec Nikto, mon adresse IP est bien dans le viseur de CrowdSec puisqu'il a décidé de bannir mon adresse IP. Cependant, l'adresse IP n'est pas bloquée. En effet, CrowdSec doit s'appuyer sur un Bouncer pour appliquer la décision et bannir l'adresse IP.
sudo cscli decisions list
On peut voir aussi que mon analyse avec Nikto a généré plusieurs alertes, ce qui donne des indications sur les types d'attaques détectés (reason).
cscli alerts list
B. Installation de PHP Composer
Pour déployer le Bouncer PHP sur son serveur, il faut installer Composer sinon il ne s'installera pas correctement. Pour l'installer, nous avons besoin de deux paquets : php-cli et unzip, que l'on va installer sans plus attendre :
sudo apt-get update sudo apt-get install php-cli unzip
Ensuite, il faut se positionner dans son répertoire racine et récupérer l'installeur avec Curl :
cd ~ curl -sS https://getcomposer.org/installer -o composer-setup.php
Une fois qu'il est récupéré, il faut vérifier que le hash SHA-384 correspond bien à l'installeur que l'on vient de télécharger. Cela permet de s'assurer de l'intégrité du fichier. On stocke le hash dans une variable nommée "HASH" :
HASH=`curl -sS https://composer.github.io/installer.sig`
Puis, on lance la commande fournie sur le site officiel de Composer qui va permettre de vérifier le hash avant que l'installation soit lancée :
php -r "if (hash_file('SHA384', 'composer-setup.php') === '$HASH') { echo 'Installer verified'; } else { echo 'Installer corrupt'; unlink('composer-setup.php'); } echo PHP_EOL;"
Vous devez obtenir le message "Installer verified". Sinon, cela signifie que votre installeur est corrompu, vous devez relancer le téléchargement.
Enfin, lancez l'installation de Composer :
sudo php composer-setup.php --install-dir=/usr/local/bin --filename=composer
Pour vérifier que l'installation est opérationnelle, exécutez simplement :
composer
Vous devez obtenir ceci :
Composer est correctement installé, vous pouvez passer à l'installation du Bouncer en lui-même.
C. Mise en place du Bouncer PHP
Souvenez-vous du premier tutoriel sur CrowdSec où j'utilisais un serveur NginX. J'avais utilisé le Bouncer NginX directement pour bloquer les adresses IP que CrowdSec souhaitait bannir. Cette fois-ci, comme nous n'utilisons pas NginX mais Apache, on va se tourner vers un autre Bouncer : PHP.
Note : le Bouncer PHP développé par CrowdSec prend en charge de nombreuses versions (PHP 7.2.x, 7.3.x, 7.4.x et 8.0.x).
Nous avons besoin de Git pour installer ce Bouncer afin de cloner le projet. Pour installer Git :
sudo apt-get install git
Ensuite, on récupère le projet en le clonant en local :
git clone https://github.com/crowdsecurity/cs-php-bouncer.git
On obtient un dossier nommé "cs-php-bouncer" dans lequel on va se positionner :
cd cs-php-bouncer/
Puis, on lance le script d'installation (il ne doit pas être lancé avec le compte "root", ni "sudo" !) en spécifiant que l'on utilise le serveur Web "Apache" :
./install.sh --apache
Enfin, on suit les instructions qui s'affichent à l'écran pour finaliser l'installation. On définit comme propriétaire sur le dossier "/usr/local/php/crowdsec/" l'utilisateur associé à Apache. Par défaut, il s'agit de l'utilisateur "www-data".
sudo chown www-data /usr/local/php/crowdsec/
Puis, on termine en rechargeant la configuration d'Apache :
sudo systemctl reload apache2
Si on liste les Bouncer installés sur notre serveur, on va voir qu'il apparaît dans la liste :
sudo cscli bouncers list
D. Deuxième scan avec Nikto : le bouncer PHP va-t-il se montrer intraitable ?
Cette fois-ci, on devrait être en mesure de détecter l'attaque, de décider de bannir l'adresse IP de l'hôte "Nikto" et surtout d'appliquer la décision grâce au Bouncer PHP. C'est ce que nous allons vérifier.
On peut commencer par utiliser Curl pour vérifier que l'on obtient bien un code de retour HTTP 200.
curl -I it-connect.tech
Puis, on lance notre scan Nikto :
nikto -h it-connect.tech
Cette fois-ci, le scan mouline un peu... Comme s'il était bloqué par quelque chose. Si on force son arrêt (CTRL+C) et que l'on relance la commande Curl, on peut voir que le code de retour HTTP a changé : HTTP/1.0 403 Forbidden. Ce qui correspond à un accès refusé sur la page, y compris si l'on essaie d'accéder au serveur avec l'adresse IP au lieu du nom.
Si j'essaie d'accéder au site avec un navigateur, on peut voir qu'un message s'affiche pour indiquer que mon adresse IP a été bloquée par CrowdSec ! Le Bouncer PHP est entré en action !
Je vous rappelle que l'on peut débannir manuellement une adresse IP avec la commande suivante (nous allons utiliser cette commande dans la suite du tutoriel) :
sudo cscli decisions delete --ip X.X.X.X
De la même façon, on peut aussi bannir manuellement une adresse IP :
sudo cscli decisions add --ip X.X.X.X
V. Bouncer PHP : mise en place d'un captcha
Avec la configuration actuelle, l'adresse IP sera bloquée pour une durée de 4 heures et l'utilisateur n'a pas de possibilité d'être débloqué pendant ce temps. À moins de prendre la peine de contacter le Webmaster du site...
Pour limiter l'impact des éventuels faux positifs sur la règle "bad user agents", nous allons présenter un captcha à l'utilisateur bloqué, plutôt qu'un blocage pur. De cette façon, un robot sera bloqué tandis qu'un utilisateur réel pourra déverrouiller l'accès au site. Nous allons réserver le même sort au crawlers malveillants (robots d'exploration).
Pour cela, il faut éditer le fichier de configuration nommé "profiles.yaml", qui est au format YAML, comme son nom l'indique.
sudo nano /etc/crowdsec/profiles.yaml
Ce fichier contient une configuration par défaut qui sert à bloquer les adresses IP pendant 4 heures, peu importe le type d'attaque détectée. Nous souhaitons conserver ce comportement, à l'exception de deux types d'alertes :
- http-crawl-non_statics
- http-bad-user-agent
Il faut que l'on ajoute notre configuration au début du fichier, et non pas à la fin. À ce jour, CrowdSec gère la priorité par rapport à l'ordre dans le fichier puisque dès que l'on match sur une règle, on ne vérifie pas la suite (un peu sur le même principe que les ACL sur un routeur, finalement) compte tenu de la présence de la directive "on_success: break" (voir ci-dessous).
Autrement dit, si l'on ajoute notre configuration spécifique à la suite dans le fichier profiles.yaml, cela ne fonctionnera pas, car la première règle correspondra (matchera) toujours, et donc notre règle ne sera pas prise en compte.
Voici le bout de code à ajouter au tout début du fichier :
# Bad User agents + Crawlers - Captcha 4H
name: crawler_captcha_remediation
filters:
- Alert.Remediation == true && Alert.GetScenario() in ["crowdsecurity/http-crawl-non_statics", "crowdsecurity/http-bad-user-agent"]
decisions:
- type: captcha
duration: 4h
on_success: break
---
La partie "filters" sert à spécifier que l'on cible seulement les scénarios d'alertes "http-crawl-non_statics" et "http-bad-user-agent". Ensuite, au niveau du "type", on spécifie "captcha" au lieu de "ban" qui est l'action standard.
Enfin, la partie "duration" sert à spécifier la durée pendant laquelle on présente un captcha à l'adresse IP associée au "blocage". En l'occurrence, 4h par défaut, mais vous pouvez modifier cette valeur.
Au final, vous obtenez ceci :
Sauvegardez le fichier et fermez-le. Il ne reste plus qu'à relancer CrowdSec :
sudo systemctl restart crowdsec
Avant de refaire des tests, on va supprimer la décision qui s'applique actuellement sur notre adresse IP, c'est-à-dire "ban". On utilise la commande ci-dessous en remplaçant "X.X.X.X" par l'IP publique correspondante.
sudo cscli decisions delete --ip X.X.X.X
Ensuite, il faut que l'on simule un accès depuis un user-agent non autorisé, car si j'utilise Firefox, Chrome, Brave, Edge, etc... cela ne fonctionnera pas, car ce sont des user-agent légitimes. Pour cela, on peut se référer à la liste de user-agents malveillants relayée par CrowdSec sur GitHub. Pour l'outil à utiliser, on partira sur l'excellent Curl que l'on a déjà utilisé précédemment.
Nous allons utiliser le modèle de commande suivant :
curl -I it-connect.tech -H "User-Agent: <nom-user-agent>"
Voici quelques exemples :
curl -I it-connect.tech -H "User-Agent: Cocolyzebot" curl -I it-connect.tech -H "User-Agent: Mozlila" curl -I it-connect.tech -H "User-Agent: OpenVAS" curl -I it-connect.tech -H "User-Agent: Nikto"
En effectuant deux-trois requêtes Curl à destination de notre site, on se retrouve rapidement avec un code d'erreur HTTP : 401 Unauthorized. Comprenez accès non autorisé.
Maintenant que je suis bloqué par CrowdSec, on va utiliser un navigateur classique pour voir comment réagit le serveur Web.
Aaaaah ! Bonne nouvelle ! Une page avec un captcha s'affiche ! Pour outrepasser le blocage, il suffit de compléter le captcha, de cliquer sur "Continue", afin d'accéder au site.
Si on liste les décisions actives, on peut voir que mon adresse IP est bien associée à l'action "captcha" au lieu de l'action "ban". On peut voir aussi que mon instance CrowdSec a banni quelqu'un d'autre, qui visiblement, s'en prend à mon serveur Web.
sudo cscli decisions list
Si l'on effectue un scan avec Nikto comme nous l'avons fait à plusieurs reprises dans cette démonstration, nous n'aurons pas le captcha, nous allons être directement bannis. En fait, il y a bien un user-agent Nikto, mais le scan avec cet outil va générer d'autres alertes avant celle sur le user-agent, ce qui implique que c'est l'action "bannir" qui va s'appliquer avant l'action "captcha". En soi, ce n'est pas plus mal, car quelqu'un qui scanne notre serveur Web, on préfère autant qu'il soit complètement bloqué.
VI. Conclusion
Voilà, nous venons de voir comment sécuriser une application ou un site Internet qui tourne sur un serveur Web Apache + PHP en détectant et bloquant les attaques avec CrowdSec. On peut voir que CrowdSec est un outil efficace et personnalisable grâce à la mise en place du captcha.
Je vous rappelle que vous pouvez démarrer un container Metabase pour accéder à un dashboard afin de visualiser de beaux graphiques et avoir des métriques précises quant à l'activité de CrowdSec sur votre serveur (voir mon précédent tutoriel).
Pour finir, quelques liens :
Propre comme dab
Merci
Bonjour 🙂
meilleurs voeux en cette nouvelle année !
Je viens de tester l’install et un problème est rencontré lors du restart apache.
crowdsec/vendor/autoload not found
et effectivement au niveau du listing de fichier pas de vendor/autoload
be carefull identation is very import for profil.yaml
name: crawler_captcha_remediation
filters:
– Alert.Remediation == true && Alert.GetScenario() in [« crowdsecurity/http-crawl-non_statics », « crowdsecurity/http-bad-user-agent »]
decisions:
– type: captcha
duration: 4h
on_success: break
—
all space are needed
this won’t work :
# Bad User agents + Crawlers – Captcha 4H
name: crawler_captcha_remediation
filters:
– Alert.Remediation == true && Alert.GetScenario() in [« crowdsecurity/http-crawl-non_statics », « crowdsecurity/http-bad-user-agent »]
decisions:
– type: captcha
duration: 4h
on_success: break
—
Effectivement, faire attention à l’indentation dans le fichier profiles.yaml avec le copier/coller.
Pour corriger le problème, vérifier l’indentation utilisée sur la configuration déjà présente et la reproduire pour les nouveaux éléments de configuration.
Ne marche pas pour moi impossible de lancer le service crowdsec et de faire du coup l’une des premières commande :
root@Canna-pi:/home/pi/Downloads# sudo cscli decisions list
FATA[13-03-2022 02:22:15 PM] Unable to list decisions : performing request: Get « http://127.0.0.1:8080/v1/alerts?has_active_decision=true&include_capi=false&limit=100 »: http code 404, invalid body: invalid character ‘<' looking for beginning of value
Je viens de trouver le problème : mon serveur tomcat tourne sur le port 8080 je dois donc changer la configuration de crowdsec pour changer les port dans ces deux fichier :
nano /etc/crowdsec/local_api_credentials.yaml
nano /etc/crowdsec/config.yaml
Autre erreur au point IV. A. commade : nikto -h nomdedomaine.fr
Si le serveur kali linux et le serveur apache sont sur le meme réseau cela ne marche pas car par défault crowdsec ne bloque pas les IP local !
L’etape V mais en erreur mon crowdsec impossible de le redémarré par contre en recherchant vite fais sur google et copiant les commandes du site officiel le redémarrage fonctionne. Cela viens probablement du fait que ton site change les » en » :
name: crawler_captcha_remediation
filters:
– Alert.Remediation == true && Alert.GetScenario() in [« crowdsecurity/http-crawl-non_statics », « crowdsecurity/http-bad-user-agent »]
decisions:
– type: captcha
duration: 4h
on_success: break
—
J’ai tester de bannir manuellement l’adresse ip de mon téléphone mais je peux toujours accédé a la page web, crowdsec ne semble pas faire sont travail comment je peux vérifier que le traffic vers les site est bien impacté par le logiciel ?
Impossible d’accédé a ma page php et dans mes log apache error.log je retrouve :
PHP Fatal error: require_once(): Failed opening required ‘/usr/local/php/crowdsec/vendor/autoload.php’ (include_path=’.:/usr/share/php’) in /usr/lo$
De l’aide pour corriger le problème ?
pendant l’install du bouncer la commande ./install.sh est impossible il me dit qu’il ne trouve pas le fichier
Meme soucis pour moi.
Du coup, je suis passé par un autre tuto pour installer le Bouncer
https://github.com/crowdsecurity/cs-standalone-php-bouncer/blob/main/docs/INSTALLATION_GUIDE.md#install-crowdsec
Cela fonctionne;