Un tableau de bord PHP qui transforme les logs bruts du WAF en renseignements exploitables : évaluation des risques en temps réel, enrichissement géographique et blocage automatique des IP via firewalld — sans aucune infrastructure externe.

Toute application web exposée à l'internet public reçoit, dans les minutes suivant sa mise en ligne, un flux constant de requêtes automatisées : des scanners de vulnérabilités à la recherche d'exploits connus, des bots récoltant du contenu, des tentatives de force brute contre les formulaires de connexion, et des requêtes de reconnaissance cartographiant la structure du site. La réponse standard à cette réalité est de déployer un Web Application FirewallUn WAF (Web Application Firewall) est une couche de sécurité placée entre internet et l'application web, inspectant le trafic HTTP et bloquant les requêtes malveillantes selon des règles configurables. — un WAF — qui filtre le trafic selon des règles prédéfinies. Mais un WAF seul génère des données, pas de la compréhension. Les logs s'accumulent, le stockage croît, et dans la plupart des cas personne ne les consulte avant que quelque chose ne soit déjà allé de travers.

C'est le problème que j'ai voulu résoudre. SecureLog est un tableau de bord WAF que j'ai conçu et construit pour transformer les données brutes des logs HTTP en intelligence opérationnelle : un système qui ne se contente pas d'afficher ce qui se passe sur votre infrastructure web, mais l'analyse, l'évalue et — lorsque les preuves le justifient — agit de manière autonome en bloquant les adresses IP malveillantes au niveau du pare-feu.

Dans cet article, je décris l'architecture, les raisonnements derrière les choix de conception, et la manière dont chaque composant collabore pour créer un système de surveillance de la sécurité en boucle fermée.

Le problème : des logs sans contexte

Un serveur web LAMP/LEMPLAMP désigne Linux, Apache, MySQL, PHP. LEMP remplace Apache par Nginx. Les deux sont des piles serveur standard pour l'hébergement d'applications web. typique génère des journaux d'accès au Combined Log FormatUn format de journal standard utilisé par Apache et Nginx. Chaque ligne enregistre l'IP du client, l'horodatage, la méthode HTTP, l'URI, le code d'état, la taille de la réponse, le referrer et la chaîne user agent. : une ligne par requête, contenant l'IP du client, l'horodatage, la méthode HTTP, l'URI demandé, le code d'état, le referrer et le user agent. Sur un site modérément fréquenté, cela représente des dizaines de milliers de lignes par jour.

Les données brutes sont là, mais en extraire du sens est un défi entièrement différent. Parmi ces 30 000 requêtes quotidiennes, lesquelles sont des visiteurs légitimes ? Lesquelles sont des robots d'indexation ? Lesquelles cherchent /wp-login.php sur un site qui n'utilise pas WordPress ? Lesquelles tentent une injection SQLUne technique d'attaque consistant à insérer des instructions SQL malveillantes dans les champs de saisie ou les URL, dans le but de manipuler ou d'extraire des données de la base de données de l'application. via les paramètres de requête ? Et surtout — quelles adresses IP le font avec une persistance suffisante pour justifier un blocage au niveau du pare-feu ?

Répondre à ces questions manuellement n'est pas viable. J'avais besoin d'un pipeline automatisé capable d'ingérer les logs, de les enrichir avec des données d'analyse des menaces et de géolocalisation, de présenter les résultats via une interface intuitive, et de fournir des capacités de réponse tant manuelles qu'automatisées.

Vue d'ensemble de l'architecture

SecureLog repose sur un backend PHP avec un frontend JavaScript, conçu pour fonctionner aux côtés du serveur web qu'il surveille. L'architecture suit une séparation claire des responsabilités à travers six composants, chacun implémenté comme un fichier distinct avec une tâche spécifique.

Le frontend (index.php) est une application mono-page avec une structure de navigation par barre latérale, construite en JavaScript pur et Chart.jsUne bibliothèque JavaScript open-source pour la création de graphiques réactifs et animés. SecureLog l'utilise pour les courbes de tendance du trafic et les visualisations de la distribution des codes d'état HTTP.. Il communique de manière asynchrone avec le backend via une passerelle API unifiée (api.php) qui achemine toutes les requêtes AJAXAsynchronous JavaScript and XML — une technique pour effectuer des requêtes HTTP depuis le navigateur sans recharger la page, permettant des mises à jour de données en temps réel dans le tableau de bord. vers la fonction de traitement appropriée. L'authentification et la gestion des sessions sont assurées par un module de sécurité dédié (auth.php et login.php), qui impose HTTPS, des paramètres de cookies sécurisés et la validation du jeton CSRFCross-Site Request Forgery : une attaque qui pousse le navigateur de l'utilisateur à effectuer des requêtes indésirables. SecureLog s'en protège en générant et en validant un jeton unique pour chaque session..

Côté traitement backend, un processeur de logs CLI (LogProcessor.php) s'exécute comme tâche planifiée, lisant les journaux d'accès bruts de manière incrémentale et passant chaque ligne à travers le moteur d'analyse des risques (RiskAnalyzer.php). Enfin, un script de synchronisation du pare-feu (SynchronizeFirewall.sh) comble l'écart entre les décisions de l'application et la configuration de firewalldL'outil de gestion de pare-feu par défaut sur les systèmes RHEL/CentOS/AlmaLinux. Il prend en charge les ipsets — des collections nommées d'adresses IP utilisables dans les règles de pare-feu pour un blocage massif efficace. du système d'exploitation, appliquant les blocages et déblocages en temps réel.

Le moteur d'analyse des risques

Au cœur de SecureLog se trouve la classe RiskAnalyzer — le composant qui transforme une ligne de log brute en une évaluation quantifiée de la menace. La philosophie de conception est directe : chaque requête reçoit un score de risque numérique, calculé en évaluant de multiples signaux indépendants et en additionnant leurs contributions pondérées.

L'analyse procède par six vérifications séquentielles :

Détection de scanners agressifs. La chaîne user agent est comparée à une bibliothèque configurable de signatures de scanners malveillants connus — des outils comme Nikto, sqlmap, DirBuster et d'autres utilitaires de reconnaissance. Une correspondance positive contribue un composant de score élevé, car la simple présence de ces outils indique une activité de sondage délibérée.

Reconnaissance de schémas d'attaque. L'URI complet de la requête est scanné à la recherche de signatures de vecteurs d'attaque courants : fragments d'injection SQL (UNION SELECT, OR 1=1), séquences de traversement de répertoiresUne attaque utilisant des séquences comme ../../ dans les URL pour sortir du répertoire racine web et accéder à des fichiers arbitraires sur le serveur, comme /etc/passwd., charges utiles XSSCross-Site Scripting : une attaque où du JavaScript malveillant est injecté dans des pages web vues par d'autres utilisateurs, typiquement via des paramètres d'URL ou des champs de formulaire. et tentatives d'injection de commandes. La bibliothèque de signatures est chargée depuis un fichier de configuration centralisé et compilée en expressions régulièresDes expressions de correspondance de motifs utilisées pour identifier des chaînes correspondant à une structure spécifique. RiskAnalyzer compile toutes les signatures de chaque catégorie en une seule regex pour la performance, en utilisant preg_quote() pour un échappement sûr. optimisées au démarrage.

Sondage de fichiers sensibles. Les requêtes ciblant des fichiers qui ne devraient jamais être accessibles de l'extérieur — fichiers de configuration, archives de sauvegarde, répertoires de contrôle de version, exports de base de données — sont signalées avec un score dédié. Cela intercepte la phase de reconnaissance qui précède typiquement une attaque ciblée.

Analyse des anomalies du user agent. Les chaînes user agent manquantes ou suspecteusement courtes reçoivent une pénalité de score. Les navigateurs légitimes envoient toujours un user agent substantiel ; son absence est un indicateur fort d'outillage automatisé. Les bots passifs connus — robots d'indexation, services de surveillance — sont identifiés séparément et évalués à un niveau inférieur.

Analyse de la longueur de l'URI. Les requêtes avec des URI dépassant 200 caractères reçoivent un composant de score supplémentaire. Des URI anormalement longs sont caractéristiques des attaques par injection, où la charge utile est intégrée dans la chaîne de requête.

Évaluation du code d'état HTTP. Des réponses 404 répétées depuis la même IP suggèrent un sondage systématique de chemins inexistants — une technique d'énumération classique.

Les six scores individuels sont additionnés en un score de risque composite, qui est ensuite classé en cinq niveaux de gravité : CLEAN, LOW, MEDIUM, HIGH et CRITICAL. Les seuils de chaque niveau sont définis dans la configuration centralisée, rendant le système ajustable sans modification du code.

Le pipeline de traitement des logs

Le script LogProcessor.php est l'épine dorsale opérationnelle du système. Il s'exécute comme processus CLICommand Line Interface — le script est conçu pour être exécuté depuis le terminal du serveur ou par un planificateur cron, pas via un navigateur web. Il refuse explicitement l'exécution HTTP pour des raisons de sécurité., typiquement déclenché par cronUn planificateur de tâches basé sur le temps sous Unix/Linux. Le processeur de logs de SecureLog est typiquement planifié pour s'exécuter toutes les quelques minutes, assurant une analyse quasi temps réel sans consommation permanente de ressources. à intervalles réguliers, et effectue trois phases distinctes en séquence.

Phase 1 : Importation incrémentale des logs. Le processeur lit les fichiers de journaux d'accès pour chaque site surveillé, en partant de la dernière position d'offset connue. C'est un choix de conception critique — le système ne retraite jamais des données déjà vues, ce qui maintient des temps d'exécution prévisibles quelle que soit la taille du fichier de log. Chaque ligne est analysée avec une approche hybride : le script tente d'abord un parseur regex standard pour le Combined Log Format, et se rabat sur un parseur à délimitation par tabulation lorsque le format dévie. Les données analysées sont normalisées : adresses IP, user agents et referrers sont résolus en identifiants internes via un cache en mémoireUn tableau associatif PHP qui stocke les recherches en base de données précédemment résolues (sites, IP, user agents, referrers) pour la durée du lot. Cela élimine les requêtes SELECT redondantes lorsque la même IP ou le même user agent apparaît des milliers de fois dans un même fichier de log. adossé à des recherches en base de données, minimisant les requêtes redondantes. Chaque ligne passe ensuite par le RiskAnalyzer, et tant l'enregistrement d'accès que son évaluation de risque sont insérés dans la base de données via des opérations par lotsAu lieu d'exécuter un INSERT par ligne de log, le processeur accumule les enregistrements et les exécute en lots configurables. Cela réduit considérablement le nombre d'allers-retours avec la base de données et améliore le débit sur les gros fichiers de logs. pour l'efficacité.

Phase 2 : Résolution de la géolocalisation. Après la fin de la phase d'importation, le processeur identifie toutes les adresses IP dépourvues de données géographiques et les résout via des appels API concurrents à un service de géolocalisation. Les résultats — pays, ville, FAI et numéro de système autonomeUn ASN (Autonomous System Number) identifie l'opérateur réseau responsable d'un bloc d'adresses IP. Connaître l'ASN aide à distinguer le trafic provenant de fournisseurs d'hébergement légitimes, de services cloud et de réseaux notoirement malveillants. — sont stockés aux côtés de l'enregistrement IP. Cet enrichissement est essentiel pour les capacités d'analyse géographique du tableau de bord et pour identifier des schémas tels que le trafic d'attaque concentré depuis des régions spécifiques.

Phase 3 : Traitement automatisé des blocages. La phase finale est celle où l'analyse devient action. La classe BanProcessor interroge la base de données des événements de risque à la recherche d'adresses IP dont l'activité de menace cumulée dépasse des seuils configurables. Les seuils sont différenciés par gravité : un seul événement CRITICAL peut suffire à déclencher un blocage, tandis que les événements de gravité LOW nécessitent un nombre plus élevé dans une fenêtre de rétrospectionUne fenêtre temporelle configurable (par défaut : 24 heures) dans laquelle les événements de risque sont comptabilisés. Les événements antérieurs à cette fenêtre sont exclus du calcul des seuils, empêchant des données obsolètes de déclencher des blocages sur des IP dont le comportement a changé. définie — typiquement 24 heures. Lorsqu'une IP franchit le seuil, le processeur met à jour son statut à BANNED et positionne l'indicateur de synchronisation du pare-feu à PENDING, signalant que le changement n'a pas encore été appliqué au niveau du système d'exploitation.

L'ensemble de l'exécution est protégé par un mécanisme de fichier verrouUne technique d'exclusion mutuelle : le script crée un fichier temporaire au démarrage et le supprime à l'achèvement. Si le fichier verrou existe déjà, le script s'interrompt, empêchant deux instances de traiter les mêmes données simultanément. qui empêche le chevauchement d'instances concurrentes — une garantie essentielle lorsque le traitement est planifié à intervalles fréquents.

Synchronisation du pare-feu

Le pont entre les décisions de SecureLog et l'application effective au niveau réseau est SynchronizeFirewall.sh — un script Bash qui lit l'état de la base de données et applique les changements à la configuration firewalld du serveur via les ipsetsDes collections nommées d'adresses IP gérées par firewalld. SecureLog maintient deux ipsets : bad_ips pour les adresses IPv4 et bad_ipv6s pour les adresses IPv6. L'appartenance à ces ensembles déclenche des règles de pare-feu bloquant tout le trafic des IP répertoriées..

La synchronisation opère en quatre phases, gérant IPv4 et IPv6 séparément pour s'adapter aux différentes familles d'ipsets requises par firewalld. Pour chaque version du protocole, le script traite deux opérations : l'ajout (application de nouveaux blocages) et la suppression (levée des blocages pour les IP dont le statut est passé à ALLOWED ou WHITELISTED). Dans tous les cas, le script interroge exclusivement les enregistrements où firewall_status = 'PENDING', ne traite que ces changements, et met à jour le statut à APPLIED après une exécution réussie.

Cette conception — séparer la décision (quoi bloquer) de l'application (comment bloquer) — offre plusieurs avantages. L'application PHP n'a jamais besoin de privilèges root ; elle écrit uniquement dans la base de données. Le script Bash, qui nécessite des permissions élevées pour modifier firewalld, opère sur une interface minimale et bien définie : une seule colonne de base de données qui fait office de file d'attente de messages. Un mécanisme de sécurité garantit qu'une « IP sûre » prédéfinie — typiquement l'adresse de l'administrateur — n'est jamais ajoutée à une liste de blocage, indépendamment de ce que produit l'analyse des risques.

Après le traitement de tous les changements en attente, le script déclenche un firewall-cmd --reload uniquement si au moins une modification a été effectuée, et produit un rapport récapitulatif de toutes les opérations réalisées.

L'interface du tableau de bord

Le frontend est une application mono-page structurée autour de cinq onglets fonctionnels, accessibles via une barre de navigation latérale sombre. L'ensemble du flux de données est asynchrone : chaque vue se remplit par des appels API à api.php, et l'interface supporte le deep linking et la navigation dans l'historique du navigateur via un système de routage d'URLLe tableau de bord utilise l'API HTML5 History (pushState/popState) pour maintenir des URL accessibles par signet pour chaque onglet et état de filtre. Naviguer vers /dashboard/?tab=logs&ip=1.2.3.4 restaure la vue exacte. personnalisé.

Vue d'ensemble. L'onglet principal présente huit indicateurs clés de performance dans une grille de cartes : total des requêtes, événements critiques des sept derniers jours, IP bloquées, IP uniques, pays uniques, trafic utile (sept jours et total) et menaces totales. Sous les KPI, trois panneaux de visualisation montrent la tendance du trafic de la semaine passée en graphique linéaire, la distribution des codes d'état HTTP en graphique en anneau, et un classement des dix pages les plus visitées. Un quatrième panneau — le tableau de décomposition des risques — affiche le nombre d'événements par catégorie de menace (scanners agressifs, schémas d'attaque, sondage de fichiers sensibles), offrant une vision directe de la nature des menaces détectées.

Trafic & Logs. Un tableau paginé et filtrable montrant les enregistrements individuels des journaux d'accès avec leurs scores de risque associés. Chaque ligne peut être développée pour afficher tous les détails, et les adresses IP sont cliquables : en sélectionner une filtre toute la vue pour montrer toutes les requêtes provenant de cette adresse. Depuis cet onglet, les IP individuelles peuvent être bloquées ou débloquées en une seule action, et les opérations en masse permettent de bloquer ou débloquer toutes les IP visibles sur la page courante ou dans l'ensemble des résultats filtrés.

Gestion des blocages. Une vue dédiée à la surveillance et à la gestion de l'inventaire complet des IP. Chaque entrée affiche l'adresse IP, son origine géographique, le score de risque moyen calculé à partir de la table risk_analysis_events, et le statut actuel (BANNED, ALLOWED ou WHITELISTED). Les filtres permettent d'isoler les IP par statut, pays ou niveau de risque. Depuis chaque entrée, l'administrateur peut naviguer directement vers l'historique complet des logs pour cette IP.

Liste noire. Une interface de blocage manuel pour ajouter des adresses IP à la liste de blocage de manière proactive — avant même que l'analyse automatisée ne les détecte. Utile pour intégrer du renseignement sur les menaces provenant de sources externes ou pour bloquer immédiatement des acteurs malveillants connus.

Liste blanche. L'inverse : un mécanisme de protection qui garantit que des adresses IP spécifiques — services de surveillance, systèmes partenaires, adresses de l'administrateur — ne sont jamais soumises au blocage automatisé, indépendamment de leurs schémas de trafic.

L'ensemble de l'interface supporte le filtrage multi-site : un sélecteur déroulant en haut de la page permet de basculer entre les données agrégées de tous les sites surveillés et des vues filtrées pour des domaines individuels. Cela rend SecureLog adapté aux environnements hébergeant plusieurs propriétés web sur la même infrastructure.

Conception de la sécurité

Un outil de surveillance de la sécurité qui serait lui-même vulnérable serait pire qu'inutile — ce serait un risque. La couche d'authentification de SecureLog implémente plusieurs mesures défensives qui reflètent cette conscience.

Les cookies de session sont configurés avec les indicateurs secure, httponly et SameSite=Lax. L'attribut httponlyUn attribut de cookie qui empêche JavaScript côté client de lire la valeur du cookie via document.cookie. C'est une défense critique contre le détournement de session via des vulnérabilités XSS. empêche JavaScript de lire le cookie de session, atténuant le détournement de session via XSS. L'attribut SameSite fournit une protection de base contre le CSRF. Tout le trafic est forcé vers HTTPS par une redirection côté serveur, et le formulaire de connexion valide un jeton CSRF à chaque soumission. Les tentatives d'authentification échouées sont enregistrées avec l'IP du client à des fins d'audit, et un délai délibéré est introduit après les tentatives échouées pour ralentir les attaques par force brute.

Côté API, chaque requête est validée pour l'authentification avant toute opération sur les données. La passerelle API renvoie des réponses JSONJavaScript Object Notation — un format léger d'échange de données. Toute la communication entre le frontend du tableau de bord et le backend api.php utilise JSON, tant pour les corps de requêtes (via POST) que pour les réponses. structurées avec des codes d'état HTTP appropriés, et les erreurs de base de données en production sont journalisées côté serveur sans exposer de détails internes au client.

Les composants CLI imposent un contexte d'exécution strict : LogProcessor.php refuse de s'exécuter en cas d'accès HTTP, et RiskAnalyzer.php bloque l'accès direct au fichier via le navigateur. Ce sont des protections simples mais essentielles contre l'exposition accidentelle.

Le modèle de données

La conception de la base de données de SecureLog suit une structure normalisée centrée sur quelques tables principales. La table ip_addresses stocke les IP uniques avec leurs métadonnées géographiques. La table ip_management suit le statut de sécurité de chaque IP (BANNED, ALLOWED, WHITELISTED) ainsi que son indicateur firewall_status (PENDING ou APPLIED) — la colonne cruciale qui pilote la boucle de synchronisation. Les journaux d'accès sont stockés avec des clés étrangères vers des tables de recherche normalisées pour les sites, adresses IP, referrers et user agents, gardant la table principale de logs compacte et efficace en termes de requêtes. Les événements d'analyse de risque sont enregistrés dans une table séparée liée au journal d'accès, permettant des requêtes agrégées par gravité, plage temporelle et IP sans scanner l'intégralité du log.

La séparation entre status (l'état souhaité) et firewall_status (l'état appliqué) est la clé de voûte architecturale de l'ensemble du système. Toutes les opérations d'écriture dans l'application PHP — qu'elles soient déclenchées par le processeur automatique de blocage, par des actions manuelles via le tableau de bord, ou par la gestion des listes blanches et noires — positionnent firewall_status à PENDING. Le script Bash de synchronisation consomme ensuite ces enregistrements en attente et les applique à firewalld. Ce motif est essentiellement une architecture événementielleUn patron de conception où les changements d'état sont enregistrés comme événements dans un stockage partagé, et des consommateurs en aval les traitent indépendamment. Dans SecureLog, la colonne de base de données fait office de file d'événements, l'application PHP de producteur, et le script Bash de consommateur. légère, implémentée sans la complexité d'une file de messages dédiée.

Pourquoi j'ai construit ce système

Dans mon travail de gestion d'infrastructure web, je me suis retrouvé à plusieurs reprises dans la même situation : le WAF faisait son travail en filtrant le trafic, les logs s'accumulaient, mais l'écart entre données brutes et prise de décision informée restait important. Les solutions SIEMSecurity Information and Event Management — des plateformes d'entreprise (telles que Splunk, IBM QRadar ou Microsoft Sentinel) qui agrègent des données de sécurité depuis de multiples sources, corrèlent les événements et fournissent des alertes. Efficaces mais typiquement coûteuses et complexes à déployer. commerciales existent, mais elles sont typiquement conçues pour des environnements d'échelle entreprise et impliquent des coûts de licence disproportionnés pour les déploiements de petite et moyenne taille. Les alternatives open-source comme la pile ELKElasticsearch, Logstash et Kibana — une plateforme open-source d'agrégation et de visualisation des logs. Puissante et flexible, mais nécessitant une infrastructure dédiée (typiquement 8+ Go de RAM pour Elasticsearch seul) et une maintenance continue. sont puissantes mais exigent une infrastructure significative et une expertise opérationnelle pour la maintenance.

Je voulais quelque chose de spécifiquement conçu : un outil pouvant être déployé aux côtés d'un serveur web existant, ne nécessitant aucune infrastructure supplémentaire au-delà d'une base de données MySQL, et capable de boucler le cycle de l'analyse des logs à l'application des règles du pare-feu sans intervention manuelle. SecureLog en est le résultat — non pas un agrégateur de logs générique, mais un système spécialisé conçu pour une mission unique et bien définie : comprendre le paysage des menaces de ses applications web et y répondre automatiquement.

Le code est structuré pour être maintenable par un seul développeur ou une petite équipe. Chaque fichier inclut une documentation inline approfondie — pas seulement des commentaires expliquant ce que fait le code, mais pourquoi certains choix ont été faits, y compris des notes datées sur les corrections de bugs et les révisions de conception. Cela reflète un principe que j'applique dans tout mon travail : un code qui ne peut pas être compris six mois plus tard par quelqu'un d'autre que son auteur est une dette technique, pas un atout.

Conclusions

SecureLog démontre qu'une surveillance efficace de la sécurité web ne nécessite pas une pile d'entreprise complexe et multi-couches. Une application PHP bien conçue, un script Bash avec les bons privilèges, une base de données servant de couche de coordination, et une séparation claire entre analyse, décision et application peuvent produire un système à la fois transparent, réactif et maintenable.

L'insight architectural clé est la boucle fermée : les logs entrent dans le système, sont analysés et évalués, les évaluations pilotent des décisions automatisées, les décisions sont synchronisées avec le pare-feu, et les résultats sont visibles en temps réel via le tableau de bord. À chaque étape, l'administrateur conserve le plein contrôle — les blocages automatisés peuvent être annulés, les listes blanches protègent les adresses de confiance, et l'historique complet de chaque décision est auditable.

Si vous gérez une infrastructure web et êtes confronté à des défis similaires — ou si vous êtes intéressé par une discussion sur les détails techniques de l'implémentation — n'hésitez pas à me contacter.