Projet de fin d’études · Dublin City University · 2024—2025
Pare-feu SMS 5G
Les SMS transportent toujours les codes à usage unique, les alertes bancaires et les liens de récupération de compte, ce qui en fait l’une des surfaces les plus attaquées d’un réseau mobile. Voici un pare-feu qui inspecte les SMS à l’intérieur d’un cœur 5G, bloque le smishing en temps réel, et a été construit puis mesuré face à une simulation fonctionnelle du réseau dans lequel il s’insérerait.
- OMNeT++ / C++
- Flask
- DistilBERT
- PostgreSQL
- Google Cloud Run
- React
- Période
- 2024 — 2025
- Cadre
- Projet de fin d’études, licence en informatique
- Établissement
- Dublin City University
- Déploiement
- Google Cloud Run, alimenté par une simulation OMNeT++
- 81,8/s
- Messages soutenus
- 1 100 ms
- 95e centile
- <100 ms
- Inférence du modèle
- 0 %
- Taux d’échec
Avec huit instances du Message Processor sous le profil de trafic élevé, sans aucune requête en échec.
Latence dans le pire cas à charge maximale, contre 2 800 ms sur une seule instance.
Temps moyen de classification DistilBERT renvoyé par Cloud Run.
Sur tous les profils de charge et toutes les configurations de service testés.
01Le problème
Le SMS est ancien, on lui fait confiance, et il reste la porte la plus facile
Presque chacun de vos comptes est joignable par un SMS. Codes à usage unique, avis de livraison, alertes bancaires, réinitialisations de mot de passe : tout arrive par un protocole conçu dans les années 1980, sans authentification de l’expéditeur et sans aucun moyen natif de distinguer une banque de quelqu’un qui se fait passer pour elle.
Les attaquants le savent. Le smishing — l’hameçonnage par SMS — fonctionne parce que le message arrive dans le même fil que les vrais, sur un appareil auquel on se fie sans y penser. À mesure que la 5G déplace la messagerie vers une infrastructure IP et augmente les volumes transportés, cette surface ne fait que croître.
Les outils que les opérateurs opposent traditionnellement au problème sont statiques : correspondances de motifs figées et listes noires tenues à la main. Ils arrêtent la campagne d’hier. Ils n’arrêtent pas un message jamais vu auparavant, rédigé par quelqu’un qui sait ce que cherchent les filtres.
Ce projet pose la question du filtre conçu pour ce problème-là : un filtre qui inspecte les messages au cœur même du réseau 5G, associe des contrôles déterministes peu coûteux à un modèle de langue qui comprend la formulation, et livre malgré tout un message légitime assez vite pour que personne ne remarque qu’il a été lu.
Smishing
Hameçonnage délivré par SMS, exploitant la confiance placée dans la boîte de réception de messages.
Appât par l’urgence
Messages conçus pour forcer un clic immédiat : compte suspendu, paiement refusé, colis bloqué.
Inondation de SMS
Rafales à haute fréquence depuis un seul expéditeur, pour des campagnes de spam ou pour épuiser les ressources du réseau.
URL malveillantes
Liens raccourcis ou sosies menant à des pages de collecte d’identifiants.
- UE
- gNodeB
- AMF
- Pare-feu
- SMSF
- UDM
02Essayez
Faites passer un message dans la chaîne de décision
Le pare-feu tranche en cinq étapes. Les trois premières s’exécutent en parallèle et peuvent arrêter un message sur-le-champ ; seul ce qui leur survit est noté, et seule une note suffisamment élevée atteint un modèle de langue. Tout ce qui suit exécute cette logique réelle dans votre navigateur.
Exemples
Envoyés dans la fenêtre: 0/8
Envoyez plusieurs fois depuis le même numéro pour déclencher la règle d’inondation.
Choisissez un exemple ou écrivez votre propre message, puis inspectez-le.
- 01—
Filtrage par règles
MSISDN sur liste noire et préfixes de pays ou de réseau bloqués, lus dans le jeu de règles en cache.
- 02—
Détection d’inondation
Messages par expéditeur dans une fenêtre glissante. La dépasser ouvre une période de refroidissement pendant laquelle tout est rejeté.
- 03—
Vérification des URL
Toute URL présente dans le message est extraite et confrontée à Google Safe Browsing.
- 04—
Moteur de notation des mots
Mots-clés pondérés, plus des pénalités pour les longues suites de chiffres et les messages inhabituellement courts.
- 05—
Classifieur DistilBERT
Un modèle DistilBERT affiné sur le jeu de données SMS Spam Collection, atteint uniquement au-dessus du seuil.
03Architecture
Trois plans, réunis par un seul appel HTTP
Le système se divise en trois plans qui montent en charge, se déploient et tombent en panne indépendamment. Un cœur 5G simulé transporte le trafic. Un ensemble de microservices cloud décide quoi en faire. Un tableau de bord côté opérateur définit les règles et observe le résultat.
Le seul couplage entre le réseau et le moteur de décision est un unique appel HTTP synchrone. C’était délibéré : le module pare-feu à l’intérieur du réseau ne sait rien de la notation, de l’apprentissage automatique ni des bases de données. Il pose une question et agit selon la réponse, ce qui permet de réécrire ou de redéployer n’importe quel service cloud sans que le réseau s’en aperçoive.
Les règles circulent dans l’autre sens, et jamais de façon synchrone. Un administrateur les édite en local, les synchronise vers Cloud SQL via un service intermédiaire, et elles sont exportées en JSON vers un bucket de stockage que le Message Processor interroge périodiquement. Rien sur le chemin du message n’attend jamais une base de données.
Cœur 5G simulé
01
Des modules OMNeT++ sur mesure en C++ et NED implémentant les fonctions réseau qu’un SMS traverse réellement, plus une interconnexion IPX pour que les messages puissent franchir les frontières entre opérateurs.
- UE
- gNodeB
- AMF
- Firewall
- SMSF
- UDM
- IPX
Plan de décision cloud
02
Des services Flask sur Google Cloud Run : le Message Processor qui prend toutes les décisions, le classifieur DistilBERT vers lequel il escalade, et un service intermédiaire qui synchronise les règles et renvoie les journaux. L’état vit dans Cloud SQL et dans un cache de règles sur Cloud Storage.
- MessageProcessor
- DistilBERT service
- RuleLogInterface
- Cloud SQL
- GCS rule cache
Plan d’administration opérateur
03
Un tableau de bord Django et React conteneurisé où les règles sont écrites et relues avant de partir en production, avec Redis et Celery absorbant le flux de journaux pour que l’ingestion ne bloque jamais l’interface.
- Django
- React
- PostgreSQL
- Redis
- Celery

04Le réseau
Un cœur 5G construit pour être attaqué
Tester un pare-feu demande du trafic, et le trafic demande un réseau. Plutôt que d’en simuler un en surface, nous avons construit un modèle fonctionnel du plan de contrôle 5G sous OMNeT++ — chaque module écrit de zéro en C++ et NED, en suivant les définitions de fonctions des 3GPP TS 23.501 et TS 33.501.
Les messages empruntent le vrai chemin. Un terminal s’attache au réseau, les tables de routage enregistrent derrière quelle station de base et quelle fonction d’accès il se trouve, puis un SMS traverse le réseau d’accès radio, la fonction d’accès, le pare-feu, la fonction SMS et la fonction de gestion des données avant de redescendre vers le destinataire.
Deux opérateurs ont été modélisés et reliés par des nœuds d’interconnexion IPX, afin d’éprouver la livraison inter-opérateurs autant que le trafic interne — le cas où un message arrive d’un réseau que vous ne contrôlez pas, d’où provient l’essentiel des abus.
Topologie, routage et calendriers de messages sont tous pilotés par des fichiers CSV et .ini. Ajouter douze terminaux, un second opérateur ou un nouveau motif de spam est un changement de configuration, pas une recompilation.
- UE (Actor)
- Un terminal, paramétré par un IMSI, un MSISDN et la section du réseau radio à laquelle il est rattaché. Émet et reçoit selon un calendrier lu dans un fichier.
- gNodeB
- La station de base 5G. Détermine si un message vient d’un terminal ou du cœur par le nom de la porte plutôt que par son indice, de sorte que la topologie peut changer sans toucher au code.
- AMF
- Gestion de l’accès et de la mobilité. Recherche derrière quelle station de base se trouve le destinataire et achemine le message, ou le dérive vers le pare-feu pour inspection.
- Pare-feu
- Un proxy entre le cœur et le cloud. Sérialise le message en JSON, le poste via libcurl, puis le transmet à la fonction SMS ou l’écarte.
- SMSF
- La fonction SMS. Livre en interne en repassant par la fonction d’accès, ou remet le message à l’IPX pour un routage inter-opérateurs.
- UDM
- Gestion unifiée des données. Conserve les fiches d’abonné et confirme le chemin de routage avant la livraison finale.
- IPX
- L’interconnexion entre opérateurs. Relaie les messages à travers les frontières de réseau et remet le trafic entrant au pare-feu de l’opérateur destinataire.


Faire parler du C++ à un service cloud
OMNeT++ n’a pas de client HTTP. Le module pare-feu utilise libcurl pour effectuer un POST synchrone vers le Message Processor sur Google Cloud, construit la charge JSON à la main et extrait le verdict de la réponse avant de décider de transmettre ou d’écarter. C’est une pièce de colle sans éclat, et c’est précisément ce qui transforme une simulation en test de bout en bout de services en production.
05Moteur de décision
Le bon marché d’abord, le modèle seulement quand il le mérite
Tout message parvenant au Message Processor doit trouver sa réponse dans la même requête. Ce budget façonne toute la conception : le travail coûteux doit rester rare, et rien de ce qui peut s’exécuter en parallèle ne devrait s’exécuter en séquence.
Trois contrôles indépendants sont lancés ensemble sur un pool de threads, et le premier blocage l’emporte : si un expéditeur est déjà sur liste noire, inutile d’attendre la fin d’une recherche d’URL. Seuls les messages qui survivent aux trois sont notés, et seuls ceux atteignant le seuil sont envoyés au modèle de langue. En pratique, l’immense majorité du trafic ne le touche jamais.
L’application des règles est volontairement indulgente. Une infraction isolée ne met personne sur liste noire : les avertissements sont comptés par catégorie, et seul un expéditeur qui franchit le seuil rejoint la liste de blocage automatique — écrite du même coup directement dans le cache de règles en mémoire, pour prendre effet au message suivant et non à la prochaine actualisation.
- Filtrage parallèle. Correspondance de règles, détection d’inondation et recherche d’URL s’exécutent simultanément sur un pool de threads, et rendent la main dès que l’une d’elles dit de bloquer.
- Notation des mots. Mots-clés pondérés, modifiables depuis le tableau de bord sur une échelle de un à dix, plus des pénalités pour les longues suites de chiffres et les messages inhabituellement courts. Vingt points déclenchent l’escalade.
- Escalade. Un modèle DistilBERT affiné sur le jeu de données SMS Spam Collection, hébergé comme son propre service Flask pour monter en charge — et tomber — indépendamment.
- Système d’avertissements. Infractions comptées par catégorie. Franchir le seuil place l’expéditeur sur liste noire et met à jour le cache de règles immédiatement.
- Limitation de débit. Une fenêtre glissante par expéditeur suivie d’un refroidissement, tenue en mémoire derrière des compteurs sûrs vis-à-vis de la concurrence, sans limiteur externe sur le chemin de la requête.
- Cache de règles. Règles téléchargées depuis un bucket de stockage au démarrage et actualisées toutes les dix minutes, en conservant le dernier jeu valide si le téléchargement échoue.
- Traitements de fin non bloquants. Les écritures en base et l’envoi des webhooks sont confiés à un pool de threads après le renvoi du verdict : un consommateur lent ne peut pas ralentir le traitement.
- Pool de connexions. Un pool partagé d’au plus quarante connexions PostgreSQL, introduit après que les premières versions eurent épuisé le serveur en en ouvrant une par message.

06Résultats
Ce qu’il fait sous charge
Les tests de charge ont utilisé Locust face aux services déployés, sur deux profils de trafic et plusieurs configurations de service. C’est le 95e centile qui est rapporté plutôt que la moyenne : ce qui compte, c’est le pire message, pas le message moyen.
50 utilisateurs simultanés, montée de 10/s
Débit
req/s · ↑
- ×120.020.0
- ×237.737.7
- ×459.259.2
- ×881.881.8
Instances du Message Processor
Latence au 95e centile
ms · ↓
- ×12,8002,800
- ×21,9001,900
- ×41,8001,800
- ×81,1001,100
Instances du Message Processor
| Instances MP | Service IA | Débit (req/s) | Latence p95 (ms) | Échecs |
|---|---|---|---|---|
| ×1 | 1 × 2 vCPU | 20.0 | 2,800 | 0% |
| ×2 | 1 × 2 vCPU | 37.7 | 1,900 | 0% |
| ×4 | 2 × 4 vCPU | 59.2 | 1,800 | 0% |
| ×8 | 2 × 4 vCPU | 81.8 | 1,100 | 0% |
- Le goulet n’était pas le modèle. L’hypothèse de départ était que le classifieur serait la contrainte. Les métriques cloud ont montré l’inverse : le service IA n’a jamais atteint son quota d’instances, même en rafale, tandis que le Message Processor saturait. La limite tenait à l’entrée, à l’évaluation des règles et au routage.
- La montée en charge était quasi linéaire. Passer de quatre à huit instances du Message Processor, sans toucher au service IA, a relevé le débit de 39 % et réduit la latence au 95e centile de plus de 35 %.
- Il est resté juste sous contrainte. Aucune requête en échec, dans aucune configuration ni sur aucun des deux profils. Rien n’a été perdu à cause de la lenteur d’un service en aval.
- La règle de dimensionnement en découle. Sous charge, ajouter des réplicas du Message Processor rapporte davantage qu’ajouter des réplicas du modèle — jusqu’à ce que le modèle lui-même sature, ce que ces tests ne sont jamais parvenus à provoquer.
07Résilience
Chaque dépendance a le droit de tomber
Un pare-feu qui cesse de laisser passer les messages parce qu’une base de données ne répond plus est devenu lui-même la panne. Chaque dépendance s’est vu attribuer un comportement de défaillance défini, et la livraison des messages ne dépend d’aucune d’elles.
| Si ceci tombe | Le système fait ceci |
|---|---|
| Stockage des règles indisponible | Se rabat sur le dernier jeu de règles mis en cache en mémoire. Le traitement continue à l’identique ; aucune règle n’est perdue, seule l’actualisation est retardée. |
| Cloud SQL injoignable | Les décisions sont prises à partir des règles en mémoire et rendues normalement. Les écritures de journal échouent silencieusement et sont consignées comme erreurs plutôt que de bloquer le verdict. |
| Classifieur IA hors ligne | L’étape d’escalade est sautée et le message passe sur son seul score. L’événement est journalisé pour que le trou reste visible après coup. |
| Point de réception du webhook injoignable | L’envoi est du « tire et oublie » sur un thread d’arrière-plan. Les échecs de livraison sont journalisés ; le tableau de bord prend du retard, pas le pare-feu. |
| Un expéditeur inonde le réseau | La fenêtre glissante bloque l’expéditeur fautif au pare-feu, avant que le message n’atteigne les fonctions d’accès, SMS ou de gestion des données — le trafic d’inondation reste entièrement hors du cœur. |
08Exploitation
La partie qu’un opérateur utilise vraiment
Un pare-feu ne vaut que par la possibilité de voir ce qu’il a fait et de changer ce qu’il fera ensuite. Le panneau d’administration est une application Django et React conteneurisée : Django sert les API et détient le jeu de règles faisant foi, React l’affiche, et Redis et Celery s’intercalent entre l’ingestion des journaux et leur traitement pour qu’une rafale de trafic s’empile au lieu de bloquer.
Les règles sont éditées en local et relues avant de partir où que ce soit. Une synchronisation pousse le jeu courant — y compris les entrées supprimées logiquement, pour que les suppressions soient versionnées plutôt que perdues en silence — vers Cloud SQL via le service intermédiaire, et une étape de déploiement distincte recharge le Message Processor.
Les journaux arrivent en continu par webhook, avec une récupération manuelle en secours si le flux s’interrompt. Chaque entrée consigne l’étape qui a tranché, la règle enfreinte, le score et la prédiction du modèle, de sorte que tout blocage peut être remonté jusqu’à sa raison.



- Table de journaux en direct. Chaque message traité avec son étape, la règle enfreinte, le score et la prédiction du modèle, filtrable par pays, statut, motif et règle.
- Étapes de traitement. Un détail par message montrant quel composant a tranché et par quel chemin.
- Vue géographique. Volume bloqué par pays sur une carte interactive, les journaux sous-jacents à un clic.
- Analyse lexicale. Quels termes apparaissent dans le trafic bloqué et le rapport bloqué/autorisé de chacun — la boucle de retour pour ajuster les pondérations.
- Gestion des règles. Seuils de spam, mots pondérés et réseaux de pays bloqués, avec des actions explicites de synchronisation et de déploiement.
- Courbes de tendance. Volume par heure, taux de blocage dans le temps, évolution des scores et fréquence de déclenchement des règles.
09Ingénierie
Ce qui a cassé, et ce qui l’a réparé
La partie intéressante d’une réalisation est rarement la conception. Voici les problèmes qui ne sont apparus qu’une fois le système en marche, et ce que chacun a changé.
01
Les connexions à la base ont été épuisées
Problème
Les premières versions ouvraient une nouvelle connexion PostgreSQL pour chaque message. Sous charge, le serveur atteignait sa limite de connexions et le traitement s’arrêtait net : délais dépassés, échecs, débit nul.
Correctif
Un pool partagé d’au plus quarante connexions réutilisées. L’épuisement est devenu impossible et le comportement sous charge concurrente, prévisible.
02
Les webhooks bloquaient le chemin de la requête
Problème
L’envoi des webhooks et la journalisation en base s’exécutaient dans le thread qui traitait le message. Un seul point de réception externe lent retardait tous les messages en file derrière lui.
Correctif
Les deux sont passés sur un pool de threads et sont émis après le renvoi du verdict. Un échec est journalisé, et rien sur le chemin du message ne l’attend.
03
BERT était trop lourd à servir
Problème
Le premier classifieur utilisait bert-base-uncased. Son empreinte mémoire et son temps d’inférence le rendaient inutilisable pour du filtrage en temps réel sur une infrastructure sans serveur.
Correctif
Passage à distilbert-base-uncased et entrée plafonnée à 25 jetons, ce qui convient à la longueur d’un SMS. L’inférence est descendue sous 100 ms sans perte de qualité de classification.
04
Les règles étaient lues à chaque message
Problème
Chaque message déclenchait une requête en base pour obtenir le jeu de règles courant. Cela ajoutait de la latence à toutes les requêtes et chargeait la base proportionnellement au trafic.
Correctif
Les règles sont exportées en JSON vers un bucket de stockage, chargées en mémoire au démarrage et actualisées toutes les dix minutes. La charge de la base a cessé de croître avec le trafic.
05
Une seule erreur vous mettait sur liste noire
Problème
Toute infraction isolée plaçait immédiatement l’expéditeur sur liste noire. Un faux positif, ou un message maladroit, coupait définitivement un abonné légitime.
Correctif
Un système d’avertissements comptant les infractions par catégorie et ne bloquant qu’au franchissement d’un seuil. L’application est restée ferme sans devenir cassante.
06
Les mises sur liste noire restaient invisibles dix minutes
Problème
Un expéditeur bloqué par le système d’avertissements était écrit en base, mais le cache de règles en mémoire l’ignorait jusqu’à sa prochaine actualisation programmée. Pendant dix minutes, un expéditeur bloqué pouvait continuer à émettre.
Correctif
Les infractions s’écrivent désormais aussi dans le cache actif, refermant la fenêtre entre l’infraction et son application.
07
La simulation cassait dès que la topologie changeait
Problème
Les modules de la fonction d’accès et de la station de base routaient par indices de portes codés en dur. Changer le nombre de terminaux ou de stations de base dans le fichier .ini provoquait des pertes silencieuses de messages ou des plantages.
Correctif
Le routage résout les portes par nom plutôt que par position. La simulation s’adapte à n’importe quelle topologie sans changement de code, ce qui a rendu possibles les scénarios multi-opérateurs de plus grande taille.
08
Le simulateur ne savait pas parler HTTP
Problème
OMNeT++ ne gère pas HTTP, alors que le moteur de décision vivait sur Google Cloud derrière une API REST. Sans pont, la simulation n’aurait jamais pu tester qu’un substitut.
Correctif
Un client libcurl à l’intérieur du module pare-feu, construisant le JSON à la main et extrayant le verdict de la réponse. La simulation pilote les vrais services déployés.
10Suite
Ce qu’il ne fait pas encore
Le système a rempli ce qu’il s’était fixé. Voici les limites que nous aborderions ensuite ; elles ont été notées comme des limites plutôt que découvertes comme des surprises.
- La confiance, pas seulement l’étiquette. Le classifieur renvoie spam ou légitime. Renvoyer en plus un score de confiance et les jetons qui l’ont motivé permettrait de mettre en quarantaine les messages limites au lieu de trancher.
- Propagation versionnée des règles. Remplacer l’actualisation toutes les dix minutes par des fichiers de règles versionnés, en basculant de version pendant un creux de trafic plutôt que sur minuterie.
- Avertissements qui s’estompent. Les avertissements sont permanents jusqu’à réinitialisation. Une pondération par gravité et une décroissance dans le temps modéliseraient mieux la réputation qu’un simple compteur.
- Notation plus fine. Le moteur de notation repère des mots isolés. Les n-grammes ou une pondération TF-IDF attraperaient des tournures que les mots pris séparément laissent passer, sans rien changer d’autre à la chaîne.
- Réentraînement sur trafic réel. Le modèle a été affiné une fois sur un jeu de données public. Le réentraîner périodiquement sur le trafic observé est l’étape évidente, et la journalisation capture déjà ce qu’il faudrait.
- Tests d’intégration automatisés. Les tests unitaires et système sont automatisés ; l’intégration entre services déployés a été testée à la main. L’automatiser est simple et a été laissé de côté faute de temps.
Technologies
Construit avec
Simulation
- OMNeT++
- INET
- Simu5G
- C++
- NED
- libcurl
Services
- Python
- Flask
- Django
- React
- Celery
- Redis
Apprentissage automatique
- DistilBERT
- Hugging Face Transformers
- PyTorch
Infrastructure
- Google Cloud Run
- Cloud SQL
- Cloud Storage
- PostgreSQL
- Docker
- GitLab CI/CD
Crédits
Équipe et documentation
Réalisé à deux comme projet de fin d’études pour la licence en informatique de Dublin City University, en travaillant conjointement sur la simulation, les services cloud et le tableau de bord.
- Avec
- Jack Keenan
- Encadrant
- Pr Mohammed Amine Togou
- Établissement
- Dublin City University, 2024—2025
- Normes
- 3GPP TS 23.501, TS 24.501, TS 33.501
- Jeu de données
- SMS Spam Collection (Kaggle), utilisé pour affiner le classifieur
- Documentation
- Spécification fonctionnelle, spécification technique, document de test et manuel utilisateur — disponibles sur demande.