Sécuriser Apache avec ModSecurity et OWASP CRS

ModSecurity ajoute une couche de filtrage applicatif devant vos sites web. Il ne remplace ni les mises à jour, ni une configuration Apache propre, ni un pare-feu réseau. En revanche, il peut bloquer ou journaliser des requêtes clairement suspectes avant qu’elles n’atteignent WordPress, PHP, un CMS ancien ou une application maison.

L’ancien tutoriel utilisait Debian Squeeze, des backports manuels et les règles Atomicorp/ASL. Cette méthode a fait son temps. Aujourd’hui, la base saine consiste à installer ModSecurity avec les paquets Debian/Ubuntu, puis à utiliser l’OWASP Core Rule Set.

Kinsta: Premium Managed WordPress hosting

ModSecurity, à quoi ça sert ?

ModSecurity est un WAF, pour Web Application Firewall. Il analyse les requêtes HTTP et peut détecter, journaliser ou bloquer des comportements suspects.

Il peut notamment aider contre :

  • les injections SQL ;
  • les attaques XSS ;
  • les tentatives de local file inclusion ;
  • les remote file inclusions ;
  • les payloads malformés ;
  • certaines attaques contre les formulaires ;
  • les scans automatisés trop bruyants ;
  • les requêtes qui ciblent des failles connues.

Le point important : ModSecurity a besoin de règles. Sans règles utiles, c’est surtout un moteur qui attend qu’on lui donne du travail. Et comme tout outil de sécurité, il peut aussi produire des faux positifs. Donc on l’installe, on observe, on ajuste, puis on bloque. Pas l’inverse.

ModSecurity 2 ou ModSecurity 3 ?

Le sujet peut vite devenir confus, donc simplifions.

  • ModSecurity 2.9.x reste très utilisé avec Apache via le module security2_module.
  • ModSecurity 3 repose sur libmodsecurity et des connecteurs séparés pour Apache, Nginx ou d’autres serveurs.
  • Sur Debian/Ubuntu avec Apache, le chemin le plus simple passe généralement par libapache2-mod-security2.

Ce tutoriel privilégie donc l’approche paquetée Debian/Ubuntu. Elle est moins sexy qu’une compilation à la main, mais elle se maintient mieux. Et sur un serveur de production, “moins sexy mais maintenable” gagne souvent par KO.

Kinsta: Premium Managed WordPress hosting

Installer ModSecurity et OWASP CRS sur Apache

Sur Debian ou Ubuntu, commencez par mettre les dépôts à jour :

sudo apt update

Installez ensuite ModSecurity pour Apache et le Core Rule Set :

sudo apt install libapache2-mod-security2 modsecurity-crs

Activez le module Apache si nécessaire :

sudo a2enmod security2

Vérifiez que le module est chargé :

sudo apachectl -M | grep security

Vous devriez obtenir une ligne proche de celle-ci :

security2_module (shared)

Avant de recharger Apache, testez toujours la configuration :

sudo apachectl configtest

Si tout va bien :

sudo systemctl reload apache2

Activer le moteur ModSecurity

Selon la distribution, le fichier recommandé peut déjà exister sous cette forme :

/etc/modsecurity/modsecurity.conf-recommended

S’il n’existe pas encore en version active, copiez-le :

sudo cp /etc/modsecurity/modsecurity.conf-recommended /etc/modsecurity/modsecurity.conf

Ouvrez ensuite le fichier :

sudo nano /etc/modsecurity/modsecurity.conf

Repérez la directive SecRuleEngine. Pour commencer prudemment, utilisez le mode détection :

SecRuleEngine DetectionOnly

Ce mode journalise les alertes sans bloquer les requêtes. C’est le meilleur point de départ sur un site existant, surtout avec WordPress, WooCommerce, un espace membre ou des formulaires complexes.

Après une phase d’observation et de réglage, vous pourrez passer en mode blocage :

SecRuleEngine On

Évitez de passer directement en blocage sur un site de production actif. Sinon, le WAF risque de confondre vos vrais visiteurs avec des gremlins. Et parfois, il aura tort.

Distingo, le livret à 2%

Charger OWASP Core Rule Set

Le paquet modsecurity-crs installe les règles OWASP CRS dans un répertoire système. Selon Debian ou Ubuntu, l’inclusion peut déjà être préparée.

Contrôlez les fichiers disponibles :

dpkg -L modsecurity-crs | less

Vous trouverez généralement les règles sous :

/usr/share/modsecurity-crs/

Sur beaucoup d’installations, Apache charge CRS via un fichier du type :

/usr/share/modsecurity-crs/owasp-crs.load

Vérifiez aussi la configuration Apache liée à ModSecurity :

grep -R "modsecurity-crs\|owasp-crs\|security2" /etc/apache2 /etc/modsecurity 2>/dev/nullLangage du code : JavaScript (javascript)

Si rien n’inclut CRS, créez un fichier dédié :

sudo nano /etc/apache2/conf-available/modsecurity-crs.conf

Ajoutez :

<IfModule security2_module>
    IncludeOptional /usr/share/modsecurity-crs/owasp-crs.load
</IfModule>Langage du code : HTML, XML (xml)

Activez ensuite cette configuration :

sudo a2enconf modsecurity-crs
sudo apachectl configtest
sudo systemctl reload apache2

Ne copiez pas les règles à la main dans /etc/apache2/modsecurity.d/ comme dans les vieux tutoriels. Vous perdriez les mises à jour propres du paquet et vous pourriez créer des doublons de règles.

Configurer CRS sans massacrer les fichiers du paquet

OWASP CRS utilise un fichier de configuration principal souvent nommé :

crs-setup.confLangage du code : CSS (css)

Selon le paquet, vous pouvez trouver un exemple avec :

sudo find /usr/share/modsecurity-crs /etc/modsecurity -name '*crs*setup*' -printLangage du code : PHP (php)

Si un fichier recommandé existe, copiez-le vers un emplacement local avant modification. Par exemple :

sudo cp /usr/share/modsecurity-crs/crs-setup.conf.example /etc/modsecurity/crs-setup.conf

Adaptez ensuite l’inclusion si votre distribution ne le fait pas déjà. Le principe reste simple : les fichiers fournis par les paquets restent intacts, vos réglages locaux vivent dans /etc/modsecurity/ ou /etc/apache2/conf-available/.

Cette séparation évite le grand classique : “j’ai mis à jour le paquet, et mes modifications ont disparu”. Le serveur n’a pas trahi. Il a juste suivi les règles du packaging.

Kinsta: Premium Managed WordPress hosting

Commencer avec un niveau de paranoia raisonnable

CRS peut fonctionner avec différents niveaux de paranoia. Plus le niveau augmente, plus les règles deviennent strictes. Et plus les faux positifs deviennent probables.

Pour un site WordPress classique, commencez en douceur :

setvar:'tx.paranoia_level=1'Langage du code : JavaScript (javascript)

Le niveau 1 suffit souvent à bloquer ou détecter beaucoup de bruit automatisé sans transformer votre site en bunker qui refuse ses propres habitants.

Ne passez pas au niveau 2 ou plus sans logs propres, exclusions ciblées et vraie phase de test. Sur WooCommerce, certains formulaires, endpoints AJAX ou appels REST peuvent déclencher des règles trop strictes.

Tester ModSecurity sans casser le site

Après installation, testez avec une requête volontairement suspecte. Depuis le serveur, vous pouvez lancer :

curl -I "http://localhost/?test=<script>alert(1)</script>"Langage du code : HTML, XML (xml)

En mode DetectionOnly, la requête ne sera pas forcément bloquée. C’est normal. L’objectif est d’abord de vérifier que ModSecurity journalise l’événement.

Surveillez les logs Apache :

sudo tail -f /var/log/apache2/error.logLangage du code : JavaScript (javascript)

Et les logs d’audit ModSecurity si activés :

sudo tail -f /var/log/apache2/modsec_audit.logLangage du code : JavaScript (javascript)

Le chemin exact peut varier selon la distribution et votre configuration. Retrouvez-le avec :

grep -R "SecAuditLog" /etc/modsecurity /etc/apache2 2>/dev/nullLangage du code : JavaScript (javascript)

Lire une alerte ModSecurity

Une alerte ModSecurity contient généralement plusieurs informations utiles :

  • l’adresse IP du client ;
  • l’URL demandée ;
  • l’identifiant de règle ;
  • le message de règle ;
  • la phase de traitement ;
  • la donnée qui a déclenché l’alerte ;
  • l’action appliquée ;
  • le score d’anomalie CRS.

Ce sont ces informations qui permettent d’ajuster proprement. Ne désactivez pas ModSecurity à la première alerte gênante. Identifiez la règle, l’URL, le paramètre concerné, puis créez une exclusion ciblée.

Passer de DetectionOnly à On

Avant de passer en blocage, testez les parcours importants du site :

  • connexion WordPress ;
  • édition d’un article ;
  • upload d’un média ;
  • soumission des formulaires ;
  • recherche interne ;
  • API REST WordPress ;
  • admin-ajax.php ;
  • commande WooCommerce ;
  • paiement ;
  • webhooks ;
  • espace client ;
  • imports CSV ;
  • tâches cron HTTP.

Si les logs restent propres, passez à :

SecRuleEngine On

Puis rechargez Apache :

sudo apachectl configtest
sudo systemctl reload apache2

Gardez les logs sous surveillance après l’activation. Un WAF se règle avec du trafic réel, pas avec de l’optimisme.

Créer une exclusion ciblée

Le mauvais réflexe consiste à désactiver une règle globalement. Le bon réflexe consiste à limiter l’exclusion à une URL, un paramètre ou un contexte précis.

Créez un fichier local pour vos exclusions :

sudo nano /etc/modsecurity/wordpress-exclusions.conf

Exemple fictif : désactiver une règle précise uniquement pour un paramètre donné sur une URL donnée :

<LocationMatch "^/wp-admin/admin-ajax\.php$">
    SecRuleRemoveById 941100
</LocationMatch>Langage du code : HTML, XML (xml)

Cet exemple n’est pas une recommandation universelle. Il illustre la logique : on retire une règle uniquement là où elle casse un flux légitime.

Ajoutez ensuite l’inclusion si nécessaire dans votre configuration Apache ou ModSecurity :

IncludeOptional /etc/modsecurity/wordpress-exclusions.conf

Puis testez :

sudo apachectl configtest
sudo systemctl reload apache2

La bonne exclusion est chirurgicale. La mauvaise exclusion ressemble à “désactiver le WAF pour tout WordPress”. Autant installer une porte blindée et laisser les clés sous le paillasson.

ModSecurity avec WordPress

WordPress fonctionne bien derrière ModSecurity, mais certains points doivent être testés avec soin :

  • /wp-admin/admin-ajax.php ;
  • /wp-json/ ;
  • l’éditeur de blocs ;
  • les sauvegardes de brouillons ;
  • les formulaires de contact ;
  • les champs HTML ou JSON ;
  • les imports ;
  • les constructeurs de pages ;
  • les plugins de sécurité ;
  • les plugins multilingues ;
  • WooCommerce et ses webhooks.

Sur un site WooCommerce, testez impérativement le tunnel de commande. Un faux positif sur le paiement coûte plus cher qu’un joli score de sécurité dans un terminal.

Si votre priorité est de protéger WordPress contre l’énumération d’utilisateurs, complétez ce WAF avec une règle dédiée côté serveur : Bloquer complètement l’énumération des utilisateurs WordPress avec Nginx ou Apache.

ModSecurity derrière Cloudflare ou un reverse proxy

Si Apache se trouve derrière Cloudflare, HAProxy, Nginx ou un autre reverse proxy, corrigez d’abord l’adresse IP réelle du visiteur. Sinon, ModSecurity verra surtout l’IP du proxy.

Avec Apache, cela passe généralement par mod_remoteip. Exemple :

sudo a2enmod remoteip

Puis une configuration adaptée à votre proxy. Pour Cloudflare, les plages IP doivent rester à jour. Ne mettez pas une vieille liste copiée depuis un forum en 2017 et oubliée dans un coin.

Ensuite seulement, analysez les logs ModSecurity. Une alerte sans vraie IP client est moins utile, surtout si vous voulez corréler avec fail2ban, Apache, WordPress ou un CDN.

Coupler ModSecurity avec fail2ban

ModSecurity bloque ou journalise des requêtes applicatives. fail2ban peut ensuite bannir temporairement les IP qui déclenchent trop d’alertes. Le duo peut être efficace, à condition de ne pas bannir les IP du reverse proxy.

Avant toute intégration fail2ban, vérifiez donc :

  • que les logs contiennent la vraie IP client ;
  • que les faux positifs sont maîtrisés ;
  • que les règles ne bannissent pas Googlebot, Stripe, PayPal ou votre CDN ;
  • que les webhooks légitimes ne déclenchent pas d’alerte en boucle.

Pour une approche serveur plus large, l’article Comment sécuriser efficacement votre serveur SSH avec SSH-Audit complète bien ce travail côté accès distant.

Performance : attention au coût des règles

Un WAF inspecte des requêtes. Il consomme donc du CPU et de la mémoire. L’impact dépend du trafic, du nombre de règles, du niveau de paranoia, de la taille des requêtes et des pages dynamiques.

Sur un petit site vitrine, l’impact peut rester discret. Sur un WooCommerce très actif, un back-office lourd ou une API très sollicitée, surveillez les temps de réponse après activation.

Commandes utiles :

sudo apachectl -M | grep security
sudo journalctl -u apache2 --since "1 hour ago"
sudo tail -f /var/log/apache2/error.log
sudo tail -f /var/log/apache2/modsec_audit.log
top
htopLangage du code : JavaScript (javascript)

Si vous auditez un site WordPress, vous pouvez aussi compléter avec Auditer les problèmes de performance WordPress avec wp profile.

Mettre à jour les règles CRS

Si vous utilisez les paquets Debian/Ubuntu, les règles CRS se mettent à jour avec le système :

sudo apt update
sudo apt upgrade

Contrôlez les versions installées :

dpkg-query -W libapache2-mod-security2 modsecurity-crs

Évitez de mélanger paquets système, règles téléchargées à la main et copies locales non suivies. C’est le meilleur moyen de créer des doublons, des erreurs de syntaxe et des configurations impossibles à maintenir.

Désactiver ModSecurity pour un vhost précis

Il peut arriver qu’un site précis ne supporte pas encore ModSecurity. Dans ce cas, ne désactivez pas tout le module globalement si les autres sites en bénéficient.

Dans le vhost concerné, vous pouvez désactiver le moteur :

<VirtualHost *:443>
    ServerName exemple.test

    <IfModule security2_module>
        SecRuleEngine Off
    </IfModule>
</VirtualHost>Langage du code : HTML, XML (xml)

Ce n’est pas idéal, mais c’est parfois utile pendant une migration. Documentez ce choix et revenez ensuite à une configuration plus fine.

Dépannage courant

Apache ne redémarre plus après activation

Lancez d’abord :

sudo apachectl configtest

Puis consultez les logs :

sudo journalctl -u apache2 --since "10 minutes ago"
sudo tail -n 100 /var/log/apache2/error.logLangage du code : JavaScript (javascript)

Les causes fréquentes : double inclusion des règles CRS, fichier local mal placé, directive non supportée, faute de syntaxe dans une exclusion ou règle copiée depuis une ancienne version.

Une page WordPress renvoie 403

Cherchez l’identifiant de règle dans les logs ModSecurity. Ensuite, créez une exclusion ciblée pour l’URL ou le paramètre fautif. Ne désactivez pas tout CRS pour une seule règle un peu nerveuse.

Le checkout WooCommerce casse

Repassez temporairement en DetectionOnly, reproduisez la commande, puis analysez les logs. Vérifiez aussi les webhooks des moyens de paiement. Stripe, PayPal et les transporteurs n’aiment pas les WAF réglés au marteau.

Les logs sont énormes

Réduisez le bruit avec une configuration d’audit raisonnable, corrigez les faux positifs récurrents et ajoutez une rotation de logs correcte. Un WAF qui remplit le disque devient lui-même un problème de production.

Checklist d’installation propre

  • Installer libapache2-mod-security2 et modsecurity-crs.
  • Activer le module Apache security2.
  • Vérifier avec apachectl -M.
  • Activer modsecurity.conf.
  • Commencer avec SecRuleEngine DetectionOnly.
  • Charger OWASP Core Rule Set sans copier les règles à la main.
  • Tester les parcours critiques du site.
  • Lire les logs ModSecurity.
  • Créer des exclusions ciblées.
  • Passer à SecRuleEngine On après validation.
  • Surveiller les erreurs 403, les webhooks et les performances.
  • Mettre à jour ModSecurity et CRS avec les paquets système.

Articles liés sur SkyMinds

Pour compléter la sécurisation d’un serveur Apache ou WordPress, ces guides peuvent vous aider :

FAQ

ModSecurity remplace-t-il un pare-feu comme nftables ou iptables ?

Non. ModSecurity travaille au niveau HTTP et applicatif. Un pare-feu réseau filtre les ports, les IP et les flux. Les deux outils sont complémentaires.

Faut-il activer ModSecurity directement en mode blocage ?

Non, pas sur un site existant. Commencez avec SecRuleEngine DetectionOnly, testez les vrais parcours utilisateurs, corrigez les faux positifs, puis passez en mode blocage.

OWASP CRS suffit-il pour protéger WordPress ?

OWASP CRS ajoute une bonne couche de défense générique, mais il ne remplace pas les mises à jour WordPress, les plugins maintenus, les permissions correctes, les sauvegardes et une configuration serveur propre.

Pourquoi ModSecurity bloque-t-il une requête légitime ?

Une règle peut confondre un contenu légitime avec un payload suspect. Lisez les logs, identifiez l’ID de règle et créez une exclusion ciblée pour l’URL ou le paramètre concerné.

Puis-je utiliser ModSecurity avec Cloudflare ?

Oui. Configurez d’abord Apache pour récupérer la vraie IP client via mod_remoteip. Sinon, vos logs ModSecurity afficheront surtout les IP Cloudflare, ce qui complique les analyses et les bannissements.

ModSecurity ralentit-il Apache ?

Oui, il ajoute un coût de traitement. L’impact dépend du trafic, du nombre de règles, du niveau de paranoia et des requêtes inspectées. Mesurez avant et après activation, surtout sur WooCommerce ou les API.

Sources

Demandez à l'IA son opinion

20 réflexions au sujet de “Sécuriser Apache avec ModSecurity et OWASP CRS”

  1. Merci pour ce billet. Installé en quelques minutes sur une Squeeze.

    J’ai eu par contre une erreur à l’étape 3 au moment de recharger la configuration d’apache et lors de l’installation des dernières règles de mod security

    Syntax error on line 37 of /etc/apache2/modsecurity.d/00_asl_z_antievasion.conf:
    Error creating rule: Unknown variable: REQBODY_ERROR
    Action 'configtest' failed.
    The Apache error log may have more information.
     failed!

    Il suffit de remplacer la variable REQBODY_ERROR par REQBODY_PROCESSOR_ERROR dans /etc/apache2/modsecurity.d/00_asl_z_antievasion.conf

    Répondre Report a typo Report icon
  2. Salut Matt,

    Que du bonheur sur ton site!

    Je viens de mettre en place ce tutoriel (les mains dans le dos), sur une nouvelle acquisition, un dédié ovh.

    Dans la série « Unknown variable ».

    En voici une autre.

    # service apache2 restart
    Syntax error on line 490 of /etc/apache2/modsecurity-rules/10_asl_rules.conf:
    Error creating rule: Unknown variable: MATCHED_VARS
    Action ‘configtest’ failed.
    The Apache error log may have more information.
    failed!
    #

    Il suffit de se rendre en ligne 490, et changer la variable MATCHED_VARS par MATCHED_VAR.

    Opération à effectué également pour les lignes :

    Syntax error on line 540 of /etc/apache2/modsecurity-rules/10_asl_rules.conf
    Syntax error on line 1187 of /etc/apache2/modsecurity-rules/10_asl_rules.conf
    Syntax error on line 1187 of /etc/apache2/modsecurity-rules/10_asl_rules.conf
    Syntax error on line 2853 of /etc/apache2/modsecurity-rules/10_asl_rules.conf
    Syntax error on line 2865 of /etc/apache2/modsecurity-rules/10_asl_rules.conf

    Ben voilà, c’est dit …

    J’t’en serre cinq, à tantôt …

    Répondre Report a typo Report icon
  3. Hello Matt,

    Pour ma part, c’est à la fin de l’étape 1 que ça a bogué!
    Sous Ubu 12, apache ne trouve pas libxml2.so.2.

    Donc —> vi /etc/apache2/mods-enabled/mod-secrity.load et remplacer le chemin LoadFile /usr/lib/libxml2.so.2 par LoadFile /usr/lib/i286-linux-gnu/libxml2.so.2.

    Et viva la musica!

    Répondre Report a typo Report icon
  4. Salut tlm, j’ai corrigé comme vous les erreurs, mais j’en ai encore :s,
    pouvez-vous m’aider?

    Syntax error on line 66 of /etc/apache2/modsecurity.d/70_asl_csrf_experimental.conf:
    Error creating rule: Unknown variable: UNIQUE_ID
    Action ‘configtest’ failed.

    Merci d’avance !

    Répondre Report a typo Report icon
    • Bonjour,

      Cela est dû au nouveau fichier (70_asl_csrf_experimental.conf) qui n’est pas vraiment stable :

      These are experimental CSRF mitigation rules. The 10_asl_rules.conf rules are designed to also handle CSRF attacks, however these rules are for more advanced environments.

      These rules work by injecting javascript code into the response, and special cookies, and must be tuned for the system to prevent false positives. If you do not understand what this means, do not use these rules.

      They are currently not supported and are experimental.

      Source

      Personnellement, je ne l’ai pas installé. Je suis sous Squeeze également.

      Répondre Report a typo Report icon
  5. Salut,

    Concernant cette erreur:

    Syntax error on line 66 of /etc/apache2/modsecurity-rules/70_asl_csrf_experimental.conf:
    Error creating rule: Unknown variable: UNIQUE_ID
    Action ‘configtest’ failed.
    The Apache error log may have more information.
    failed!

    Pour l’heure, il suffit de commenter les lignes suivantes, suivit d’un restart.

    ligne65 #SecRule RESPONSE_HEADERS:/Set-Cookie2?/ …
    ligne66 # SecRule UNIQUE_ID « (.*) » …..

    À bientôt Matt … ^¿^

    Répondre Report a typo Report icon
    • Merci loreleil ! :)

      J’ai préféré ne pas utiliser le fichier mais on peut bien sûr commenter les lignes qui posent problèmes (tout en sachant qu’il faudra refaire les changements à la prochaine mise à jour).

      Répondre Report a typo Report icon
  6. Bonjour,

    J’ai suivi le tuto, ca affiche toujours l’erreur 404, et les logs error.log:

    ModSecurity: Access denied with code 404 (phase 1). Match of "streq %{SESSION.IP_HASH}" against "TX:ip_hash" required. [file "/etc/apache2/modsecurity.d/70_asl_csrf_experimental.conf"] [line "56"] [id "340206"] [msg "Warning - Sticky SessionID Data Changed - IP Address Mismatch."] [hostname "www.domaine.com"] [uri "/"] [unique_id "USG0WCU7OmsAACHBBn0AAAAD"]

    et:

    ModSecurity: Access denied with code 403 (phase 2). Pattern match "(?:< ?object[ /+\\\\t].*?((type)|(codetype)|(classid)|(code)|(data))[ /+\\\\t]*=|< ?applet[ /+\\\\t].*?code[ /+\\\\t]*=|< ?base[ /+\\\\t].*?href[ /+\\\\t]*=|)" at REQUEST_URI_RAW. [file "/etc/apache2/modsecurity.d/15_asl_paranoid_rules.conf"] [line "469"] [id "340152"] [rev "23"] [msg "Atomicorp.com UNSUPPORTED DELAYED Rules: PARANOID MODE Cross Site Scripting Attack (IE variant)"] [hostname "www.domaine.com"] [uri "/URL.html"] [unique_id "USG0XSU7OmsAACHABQwAAAAC"]

    sachant que le mode php5 est desactive, je travaille avec suphp et suexec

    merci a vous

    Répondre Report a typo Report icon
    • Bonjour Fred,

      Les règles experimental et paranoid sont très instables : elles cassent pas mal de choses et causent beaucoup de faux-positifs.

      A ta place, je les désactiverai. C’est ce que j’ai fait chez moi, je n’ai pas vraiment le temps de les débugger (et elles changent d’une version à l’autre).

      Répondre Report a typo Report icon

Laisser un commentaire