Un site web laissé nu face à l’Internet finit tôt ou tard attaqué par des robots qui testent des failles connues, des injections SQL ou des scripts XSS. Sur un serveur Nginx, installer un WAF comme ModSecurity change le rapport de force : les requêtes HTTP sont filtrées avant même d’atteindre l’application, et une grande partie du bruit malveillant disparaît.
Couplé à l’OWASP Core Rule Set, ce pare-feu d’application couvre les attaques les plus courantes sans réécrire une ligne de code métier. L’enjeu n’est pas de cocher une case “sécurité” de plus, mais de garder une application exploitable le lundi matin quand un nouvel exploit pour une CVE critique circule déjà sur les forums.
Dans beaucoup de PME, le réflexe sécurité se limite au pare-feu réseau et au proxy. C’est déjà ça, mais cela ne voit rien des paramètres HTTP, des payloads JSON ou des chemins d’URL tordus envoyés à vos API. Un WAF côté Nginx complète ces briques en ajoutant une couche d’inspection dédiée au trafic web.
L’idée directrice de ce guide est simple : partir d’une installation propre de ModSecurity v3 sur Debian/Ubuntu, activer le jeu de règles OWASP, puis affiner la configuration pour limiter les faux positifs et préserver les performances. Tout sera illustré par un cas fil rouge, celui d’une petite plateforme de supervision industrielle qui expose une interface web et une API REST.
- Mise en place d’un WAF Nginx : installation de ModSecurity v3 et activation du module sur Debian/Ubuntu modernes.
- OWASP Core Rule Set : ajout et branchement des règles de référence pour la protection web (SQLi, XSS, RFI, CVE).
- Phase d’apprentissage : usage du mode DetectionOnly pour analyser les journaux et réduire les faux positifs.
- Ajustements avancés : niveaux de paranoïa, scoring d’anomalies, exclusions ciblées, règles personnalisées.
- Exploitation au quotidien : surveillance des logs, gestion des performances, intégration avec d’autres pare-feu et services de filtrage.
Mettre en place Nginx ModSecurity : de l’installation du WAF aux premiers tests
Avant de jouer avec les règles, il faut un socle propre. Sur un serveur Debian ou Ubuntu équipé de Nginx 1.18 ou supérieur, ModSecurity v3 se déploie aujourd’hui via les paquets standards.

C’est nettement plus simple que les montages historiques où il fallait recompiler nginx avec le connecteur ModSecurity. Cette simplification enlève une excuse souvent avancée pour repousser le sujet de la sécurité applicative.
Une petite société de maintenance industrielle, nommée NordAutom, expose un portail web pour ses clients. Le serveur tourne sur un VPS Ubuntu, géré par une équipe IT réduite. Les administrateurs ont déjà sécurisé SSH, mis en place des sauvegardes et des mises à jour régulières, mais les logs nginx montrent des requêtes étranges : scans de fichiers PHP, tentatives de login agressives, paramètres bizarres dans les URLs. L’idée est de déployer ModSecurity pour filtrer tout ce bruit sans toucher au code existant.
Sur ce type d’environnement, la séquence de départ ressemble à ceci :
1. Installation des paquets nécessaires
Après un apt update, l’installation côté distribution se résume à quelques paquets :
sudo apt install -y libmodsecurity3 libmodsecurity-dev nginx-module-modsecurity
Le module dynamique ngx_http_modsecurity_module.so est alors disponible dans le répertoire des modules Nginx. Cette approche par module dynamique limite les risques lors des mises à jour du serveur web : pas de recompilation complète, les mises à jour de sécurité de Nginx restent gérées par la distribution.
2. Activation du module dans Nginx
Le lien entre Nginx et le moteur ModSecurity se fait dans le fichier nginx.conf. Il suffit d’ajouter la directive de chargement de module, puis de déclarer un fichier de règles globaux au niveau du bloc http.
Exemple typique :
load_module modules/ngx_http_modsecurity_module.so;
Dans le bloc http :
modsecurity on;
modsecurity_rules_file /etc/nginx/modsec/main.conf;
Cette configuration indique à Nginx que chaque requête passera par ModSecurity, qui lira ses directives dans le fichier main.conf. C’est une brique centrale : un oubli ici, et tout le travail sur les règles risque de ne jamais être exécuté.
3. Création de l’arborescence de configuration
Pour garder une configuration lisible, il est judicieux de regrouper tous les fichiers liés au pare-feu applicatif dans /etc/nginx/modsec. On y copiera un fichier de base modsecurity.conf et, plus tard, le jeu de règles OWASP CRS. Cette séparation claire aide aussi lors des audits : les équipes peuvent isoler rapidement ce qui relève de la protection web.
4. Premier test fonctionnel
Avant de parler tuning, un test simple avec curl sur une URL de démonstration suffit à vérifier que le moteur ModSecurity lit bien ses règles et journalise les événements dans /var/log/modsec_audit.log. Beaucoup d’équipes s’arrêtent à “nginx -t est OK”, alors que la vraie validation passe par une requête qui déclenche volontairement une alerte, par exemple avec un paramètre ressemblant à une injection SQL.
Cette première étape donne à NordAutom un WAF présent dans la chaîne de traitement, sans encore bloquer quoi que ce soit. Autrement dit, une ceinture de sécurité bouclée mais qui ne serre pas encore.
Configurer ModSecurity pour Nginx : réglages de base, mode détection et journalisation
Avec le moteur en place, la vraie différence se joue dans la configuration initiale. ModSecurity démarre à partir d’un fichier de référence souvent nommé modsecurity.conf-recommended. Copier ce fichier dans /etc/nginx/modsec/modsecurity.conf offre un point de départ éprouvé, mais il contient encore des choix génériques qui ne collent pas toujours à une application de production.
Le cœur de ce réglage tient en une directive : SecRuleEngine. Trois modes existent : Off, DetectionOnly et On. Partir directement en On sur un serveur de production revient à serrer le frein à main sans regarder la route : quelques faux positifs suffisent à casser une fonctionnalité critique un vendredi soir. L’approche raisonnable consiste à démarrer en DetectionOnly.
SecRuleEngine DetectionOnly
Dans ce mode, toutes les règles sont évaluées, les correspondances sont journalisées, mais les requêtes ne sont pas bloquées. Pour NordAutom, cela signifie que le portail restera accessible à tous les clients pendant la phase de découverte, tout en accumulant de précieuses données pour la suite.
Pour que ces données soient exploitables, il faut aussi soigner la partie journalisation. ModSecurity possède un audit log très verbeux par défaut. Sur un environnement actif, ce niveau de détail peut vite saturer le disque ou rendre toute analyse fastidieuse. Ajuster les paramètres suivants permet de garder les informations utiles sans noyer les équipes :
- SecAuditEngine RelevantOnly pour limiter l’audit aux événements significatifs.
- SecAuditLogParts ABCFHZ pour conserver les parties utiles des requêtes.
- SecAuditLogRelevantStatus « ^(?:5|4(?!04)) » pour se concentrer sur les codes d’erreur (sauf 404 classiques).
Certains administrateurs craignent que ce filtrage ne masque des signaux faibles. En pratique, un portail métier comme celui de NordAutom génère déjà assez de bruit légitime. Mieux vaut des journaux plus courts mais exploitables qu’un flux continu jamais relu.
Une fois ces réglages en place, la phase d’observation peut démarrer. Pendant quelques jours, un grep sur “Matched” ou “Detected” dans le fichier modsec_audit.log permet d’identifier les règles les plus actives et les modèles de requêtes en cause. On découvre souvent des scripts internes un peu cavaliers, des plugins tiers bavards ou des intégrations API qui envoient des paramètres suspects.
Au passage, ces logs mettent aussi en lumière des comportements que le pare-feu réseau classique ignore : scans de vulnérabilités automatisés, bots cherchant des fichiers de configuration, ou encore tentatives de contournement de services de filtrage externes, comme cela arrive parfois sur des architectures faisant intervenir un proxy Cloudflare en amont. Sur ce point, un article détaillé sur les erreurs de routage de Cloudflare et le code erreur 1000, par exemple dans cette analyse technique, rappelle que les couches de filtrage sont complémentaires, pas interchangeables.
Pour NordAutom, cette première fenêtre d’observation révèle par exemple que l’API reçoit régulièrement des requêtes de scanners comme sqlmap. Le fait de voir noir sur blanc ces tentatives, avec les signatures associées, aide la direction à comprendre pourquoi un pare-feu applicatif n’est pas un luxe réservé aux grands groupes.
Cette étape de configuration générale se conclut quand les équipes se sentent à l’aise pour lire et interpréter les journaux. Un WAF non surveillé n’est qu’un bloc de configuration de plus ; un WAF dont les logs sont compris devient un outil de pilotage de la sécurité web.
Ajouter OWASP Core Rule Set à ModSecurity : transformer Nginx en pare-feu applicatif complet
Le moteur ModSecurity sans règles avancées, c’est un peu comme un automate industriel sans programme : puissant mais inerte. L’association avec l’OWASP Core Rule Set (CRS) apporte une vraie valeur : un ensemble de règles régulièrement mis à jour pour couvrir les principaux vecteurs d’attaque web, de l’injection SQL à la désérialisation dangereuse. C’est cette brique qui transforme réellement Nginx en WAF crédible.
Concrètement, sur le serveur Debian ou Ubuntu, l’ajout se fait en clonant le dépôt officiel dans /etc/nginx/modsec :
cd /etc/nginx/modsec
sudo git clone https://github.com/coreruleset/coreruleset.git
On renomme ou utilise tel quel le fichier de configuration de base : crs-setup.conf.example devient crs-setup.conf. Ce fichier contient des paramètres globaux comme le niveau de paranoïa, les seuils de scoring d’anomalies et quelques options de compatibilité. Les fichiers de règles (*.conf) sont ensuite inclus dans main.conf.
La structure typique du fichier /etc/nginx/modsec/main.conf ressemble alors à ceci :
Include /etc/nginx/modsec/modsecurity.conf
Include /etc/nginx/modsec/coreruleset/crs-setup.conf
Include /etc/nginx/modsec/coreruleset/rules/*.conf
À partir de là, chaque requête HTTP traitée par Nginx passe par des centaines de contrôles : patterns d’injection SQL (SQLi), tentatives de cross-site scripting (XSS), inclusions de fichiers distants (RFI), appels suspects à des shells, signatures connues pour des CVE médiatisées comme Log4Shell. NordAutom, qui expose une API JSON alimentant des tableaux de bord industriels, bénéficie soudain d’un garde-barrière qui repère les payloads les plus courants sans intervention sur l’application.
Une caractéristique intéressante du CRS est son mode de scoring d’anomalies. Plutôt que de bloquer au premier match, chaque règle ajoute des points à un score pour la requête. Quand ce score dépasse un certain seuil, la requête est marquée comme malveillante. Ce mécanisme évite de bloquer un utilisateur pour un simple caractère suspect isolé, tout en réagissant sur des combinaisons de signaux faibles.
Pour clarifier ces paramètres, le tableau suivant résume les éléments les plus souvent ajustés dans crs-setup.conf :
| Paramètre CRS | Rôle | Valeur courante recommandée | Impact sur la protection et les faux positifs |
|---|---|---|---|
| tx.paranoia_level | Niveau d’agressivité des règles (1 à 4) | 1 pour démarrer, 2 sur environnements maîtrisés | Plus le niveau monte, plus la couverture est large, mais les faux positifs augmentent nettement à partir de 3 |
| tx.inbound_anomaly_score_threshold | Seuil de blocage pour les requêtes entrantes | 5 en standard, 3 sur applications très sensibles | Un seuil bas bloque plus d’attaques mais peut gêner des usages légitimes un peu atypiques |
| tx.outbound_anomaly_score_threshold | Seuil de blocage pour les réponses sortantes | 4 en standard | Utile pour éviter les fuites de données ou refuser des réponses contenant des patterns dangereux |
| tx.enforce_bodyproc_urlencoded | Contrôle des corps de requêtes en URL-encoded | On sur formulaires classiques, Off pour APIs particulières | Si mal réglé, peut générer des erreurs sur des formats de payload peu standards |
Pour NordAutom, un choix simple consiste à démarrer avec un paranoia_level à 1 et un seuil inbound à 5, en restant en mode DetectionOnly. Après une semaine d’observation, certaines routes API sensibles peuvent monter en paranoia_level 2, avec un seuil plus bas, tout en conservant un niveau plus tolérant sur les pages publiques.
Autre point qui mérite d’être souligné : la mise à jour du CRS. Un simple git pull dans le répertoire coreruleset, suivi d’un reload Nginx, suffit pour bénéficier des dernières signatures. Dans un contexte où de nouvelles failles apparaissent régulièrement sur les CMS, frameworks et bibliothèques JavaScript, ce mécanisme joue le même rôle qu’un antivirus pour un poste utilisateur. Refuser ces mises à jour, c’est laisser un pare-feu web vieillir seul face à un environnement qui ne l’attend pas.
Un seul bémol reste fréquent : certaines équipes comptent uniquement sur un service en amont type reverse proxy hébergé, persuadées que cela remplace totalement un WAF local. Les nombreux cas de mauvaise configuration ou de filtrage partiellement contourné, documentés par des retours d’expérience comme ceux sur les blocages liés au pare-feu Cloudflare, montrent qu’un contrôle applicatif au plus près de Nginx reste pertinent. La combinaison des deux niveaux est plus robuste qu’un pari sur un seul maillon.
Une fois ce jeu de règles embarqué et compris, Nginx cesse d’être simplement un reverse proxy rapide : il devient un point de contrôle de la sécurité web à part entière, capable d’encaisser les attaques automatisées du quotidien.
Ajuster finement le WAF ModSecurity sur Nginx : faux positifs, exclusions et règles sur mesure
Dès que l’on bascule ModSecurity + CRS sur un vrai trafic, la question des faux positifs apparaît. Une recherche plein texte avec des caractères spéciaux, un export CSV un peu chargé, un back-office qui manipule des snippets HTML : ces usages peuvent ressembler à des attaques vues de loin. Un WAF utile est un WAF qui sait faire la différence entre un attaquant et un utilisateur légitime un peu créatif.
Pour NordAutom, les premiers jours de journalisation montrent que les pages d’administration de l’outil reçoivent des alertes sur certaines requêtes POST, surtout lorsqu’un technicien colle un script de diagnostic un peu verbeux dans un champ texte. Les règles 941100 et 942100 du CRS, par exemple, sont souvent en cause pour les patterns assimilés à du SQLi ou du XSS.
Deux stratégies coexistent pour traiter ces cas :
Première approche : retirer globalement des règles jugées trop bruyantes. C’est tentant, mais dangereux. Supprimer une règle à l’échelle du site enlève un filet de sécurité pour tous les endpoints, y compris ceux exposés au public ou aux intégrations non maîtrisées.
Deuxième approche : limiter l’exception à une zone fonctionnelle précise, par exemple un répertoire /admin/ ou une route /api/debug. C’est plus cohérent avec la logique de “défense en profondeur”.
Sur Nginx, ces exclusions ciblées se déclarent facilement dans le bloc server ou location via des directives modsecurity_rules. Par exemple :
location /admin/ {
modsecurity_rules ‘
SecRuleRemoveById 941100
SecRuleRemoveById 942100
‘;
}
Cette configuration indique clairement que ces règles spécifiques sont écartées uniquement pour cette section du site. L’ensemble reste actif ailleurs. C’est un compromis acceptable dans de nombreux cas, à condition de documenter ces exceptions et de les réévaluer régulièrement.
Une autre technique, plus fine encore, consiste à jouer sur le scoring. Si certaines règles génèrent de nombreux faux positifs mais apportent une information utile, il est possible de réduire leur poids dans le calcul de l’anomaly score plutôt que de les supprimer. De cette manière, elles enrichissent le signal global sans déclencher un blocage à elles seules.
Vient ensuite la question des règles personnalisées. Le CRS couvre un spectre large, mais chaque application a ses particularités : une IP interne qui ne devrait jamais appeler l’interface publique, un user-agent utilisé exclusivement par un outil de test agressif, un endpoint /login qui mérite un contrôle de rythme plus serré. Voici quelques exemples typiques :
Bloquer une IP précise
SecRule REMOTE_ADDR « @ipMatch 1.2.3.4 » « id:1000,phase:1,deny,status:403 »
Bloquer un user-agent de scanner connu
SecRule REQUEST_HEADERS:User-Agent « @contains sqlmap » « id:1001,phase:1,deny,status:403 »
Mettre en place un rate-limit sur /login
avec un compteur d’essais par adresse IP stocké par ModSecurity lui-même, puis un blocage temporaire au-delà d’un seuil. Cette logique vient compléter un système de captcha ou d’authentification multi-facteur, sans modifications profondes côté application.
Ces règles maison doivent garder une numérotation d’identifiants distincte de celles du CRS pour éviter les collisions. Un simple range dédié, par exemple à partir de 1000, suffit. Là encore, la discipline de documentation fait la différence entre un WAF maintenable et un empilement obscur de directives.
Ce travail d’ajustement donne au final une impression claire : ModSecurity n’est pas un composant que l’on installe puis que l’on oublie. C’est un outil à apprivoiser. Une fois intégré dans les rituels de l’équipe (revue régulière des logs, validation des nouvelles règles sur un environnement de préproduction), il devient une pièce stable de l’architecture, loin de l’image d’une “boîte noire” capricieuse.
Performance, périmètre et monitoring : faire vivre un WAF ModSecurity sur un serveur Nginx
Reste une question souvent posée par les responsables d’exploitation : quel prix en performance pour cette couche de protection ? Les mesures terrain montrent en général un surcoût de l’ordre de quelques millisecondes par requête, souvent dans une fourchette de 5 à 15 ms selon le niveau de paranoïa et la complexité des règles déclenchées. Sur un serveur qui met déjà 300 ms à générer une page, ce surcoût reste acceptable. Sur une API temps réel très tendue, il faut en revanche réfléchir au périmètre exact à filtrer.
Un réglage simple consiste à limiter le WAF aux endpoints dynamiques qui exposent de la logique métier. Les contenus statiques (images, CSS, JavaScript, téléchargements) peuvent être servis hors ModSecurity. Dans Nginx, cela se traduit par un modsecurity off dans les blocs location dédiés aux assets :
location /static/ {
modsecurity off;
}
Cette séparation soulage le moteur d’inspection tout en conservant l’essentiel de la sécurité là où elle compte. Pour NordAutom, les assets des tableaux de bord sont ainsi exclus du filtrage, tandis que les endpoints /api/ et /admin/ restent pleinement contrôlés.
Côté tuning, les directives comme SecRequestBodyLimit ou SecRequestBodyNoFilesLimit permettent de fixer des bornes sur la taille des corps de requêtes analysés. Inspecter un upload de plusieurs centaines de mégaoctets n’a pas grand sens ; il vaut mieux limiter l’analyse à une plage raisonnable et laisser les uploads volumineux suivre un traitement dédié. Sur un portail qui accepte des fichiers de logs ou des exports, ce réglage peut faire la différence entre un WAF fluide et un goulot d’étranglement.
La partie monitoring ne se limite pas au simple tail -f des journaux. Sur un environnement en production, trois indicateurs valent la peine d’être suivis :
Le nombre de requêtes bloquées par jour, obtenu par un grep “Access denied” dans les logs d’erreur Nginx ou directement dans le fichier d’audit. L’évolution de cette métrique renseigne sur la pression extérieure.
Le top des règles déclenchées, extrait par un grep des IDs dans modsec_audit.log. Les règles les plus bavardes méritent un examen : sont-elles trop strictes pour cette application, ou révèlent-elles une surface attaquée inattendue ?
Le top des IP ou des user-agents bloqués, pour repérer des patterns récurrents (bots, proxys mal configurés, intégrations partenaires mal traitées). Ces informations peuvent ensuite alimenter d’autres couches, comme un bannissement plus durable via iptables, Fail2ban, ou une solution participative type CrowdSec.
Il ne faut pas non plus négliger l’effet combiné avec des services de filtrage externes. Certains scénarios voient des robots tenter de contourner des protections de CDN ou de proxy web, comme ceux décrits dans des analyses techniques sur les méthodes pour dépasser certaines protections Cloudflare. Avoir un WAF local sur Nginx permet de garder la main, même si l’étage supérieur est mal configuré, saturé ou momentanément désactivé.
Dernier point, plus opérationnel : formaliser une petite check-list de commandes courantes pour l’équipe d’astreinte. Savoir tester la syntaxe de la configuration (nginx -t), recharger proprement le service, suivre en temps réel les journaux et filtrer par ID de règle ou par IP, tout cela évite de longues séances de recherche à chaud. Dans les faits, un technicien de NordAutom peut ainsi diagnostiquer en quelques minutes s’il s’agit d’un bug applicatif ou d’un blocage WAF trop strict.
Un WAF qui s’intègre dans ces réflexes quotidiens cesse d’être perçu comme un obstacle. Il devient un outil de diagnostic et d’observation, au même titre que les graphes de performances ou les traces applicatives.
ModSecurity sur Nginx remplace-t-il un pare-feu réseau classique ?
Non. ModSecurity agit comme un pare-feu d’application Web centré sur le protocole HTTP et les requêtes vers vos sites ou API. Un pare-feu réseau filtre le trafic à des niveaux plus bas (ports, IP, protocoles). Les deux sont complémentaires : le pare-feu réseau limite l’accès au serveur, le WAF contrôle ce qui entre dans votre application via HTTP(S).
Faut-il activer ModSecurity directement en mode On sur un serveur de production ?
Ce n’est pas recommandé. Le mode DetectionOnly permet d’observer les requêtes et d’identifier les faux positifs sans bloquer de trafic. Une fois les règles ajustées et les exclusions définies, le passage en mode On se fait avec beaucoup moins de risques pour les utilisateurs.
OWASP Core Rule Set suffit-il pour protéger toutes les applications web ?
Le CRS couvre un large spectre d’attaques courantes, mais ne connaît pas les spécificités de votre métier. Pour une protection solide, combinez-le avec des règles personnalisées adaptées à vos URL sensibles, à votre logique de login et à vos contraintes réglementaires. Il ne remplace pas une revue de code ni une gestion rigoureuse des mises à jour.
Quel impact ModSecurity a-t-il sur les performances de Nginx ?
Sur la plupart des déploiements, le surcoût se situe dans une fourchette d’environ 5 à 15 ms par requête, en fonction du niveau de paranoïa et du type de trafic. On peut réduire cet impact en désactivant le WAF sur les contenus statiques, en limitant l’analyse des gros corps de requête et en évitant des niveaux de paranoïa trop élevés sur des endpoints peu exposés.
Comment gérer les mises à jour du Core Rule Set sans perturber la production ?
La bonne pratique consiste à mettre à jour le dépôt CRS sur un environnement de préproduction, à surveiller les journaux en mode DetectionOnly, puis à déployer la nouvelle version en production avec un simple git pull suivi d’un reload Nginx. Documenter les règles les plus sensibles et les exclusions en place facilite ces montées de version régulières.