Discourse + Pare-feu d'application web (WAF) mod_security

Quel est le WAF recommandé à placer devant Discourse ?

J’ai récemment réussi à modifier la configuration nginx installée dans le conteneur Docker de Discourse pour y inclure ModSecurity et le CRS (Core Rule Set) de l’OWASP (Open Web Application Security Project).

Mes tests jusqu’à présent sont excellents, et ModSecurity semble fonctionner très bien avec Discourse dès la sortie de la boîte.

Quelles sont les expériences d’autres utilisateurs avec les WAF et Discourse ? Avez-vous des recommandations autres que ModSecurity ?

Une note sur l’importance des WAF : ils offrent une protection étendue contre les vulnérabilités 0-day, non seulement au niveau de l’application web elle-même, mais aussi de l’ensemble de sa pile de dépendances.

On m’a signalé que l’équipe de Discourse dispose d’un programme de bug bounty, ce qui est excellent !

…mais, bien sûr, il n’existe pas de code à 100 % sécurisé. Des erreurs surviennent, et il n’est même pas nécessaire que ce soit une erreur de votre équipe pour qu’une vulnérabilité critique soit introduite dans votre projet.

Par exemple, considérez l’histoire récente avec CVE-2019-11043 : un bug dans php-fpm qui pouvait être exploité pour exécuter du code à distance sur des serveurs exécutant des versions vulnérables de php-fpm et nginx.

Cependant, CVE-2019-11043 peut être entièrement atténué en bloquant les requêtes contenant des retours chariot ou des sauts de ligne :

Ceci n’est qu’un exemple montrant comment une installation d’application web peut être exploitée via une vulnérabilité critique, même si le code de cette application web elle-même ne présente aucun défaut. Dans le cas de Discourse, de nombreux logiciels externes sont intégrés lors de l’installation Docker, ce qui pourrait rendre Discourse vulnérable à l’avenir.

Dans le cas ci-dessus, cependant, le CRS ModSecurity bloque déjà les requêtes contenant des sauts de ligne et des retours chariot – protégeant ainsi efficacement les serveurs web autrement vulnérables à CVE-2019-11043 avant même le jour zéro.

Ce type de vulnérabilités est découvert tout le temps. L’utilisation d’un WAF comme ModSecurity pour les applications web présente des avantages significatifs.

C’est une mauvaise idée et ce n’est pas recommandé. L’intérêt d’un tel dispositif pour une application JavaScript est extrêmement limité et cela ajoute une complexité significative à votre configuration d’hébergement.

Concerne : Je viens de trouver ce guide de @joelradon sur l’installation de Discourse avec le NAXSI WAF dans nginx, avant le conteneur Docker Discourse :

C’est tout aussi impossible à prendre en charge que ce que vous demandez ci-dessus.

Si cela peut aider, je peux également ajouter une balise « non pris en charge » là-bas ?

Je pense que le désir d’un dispositif magique qui atténue automatiquement les problèmes est quelque peu erroné dans la configuration de Discourse. Nous avons un programme de primes, et nous corrigeons les problèmes dans Discourse en quelques heures seulement après leur signalement. Les sites exécutent tests-passed par défaut, ce qui, dans le cas d’aujourd’hui, inclut des commits effectués aujourd’hui.

Bien sûr, si vous exécutez un logiciel qui a été exploité il y a des années et que vous n’avez pas la liberté de le mettre à jour en raison de… raisons… un WAF peut avoir du sens car il pourrait vous sauver. Mais dans le cas de Discourse, je pense que c’est tout au plus une erreur de jugement.

J’ai pu conserver avec succès mes modifications de configuration nginx ModSecurity à travers les exécutions de launcher rebuild app comme suit :

Tout d’abord, nous mettons à jour la copie locale de install-nginx provenant du dépôt discourse_docker et clonée dans /var/discourse/.

cd /var/discourse/image/base
cp install-nginx install-nginx.`date "+%Y%m%d_%H%M%S"`.orig

# ajouter un bloc pour récupérer le module nginx ModSecurity juste avant le téléchargement du code source nginx
grep 'ModSecurity' install-nginx || sed -i 's%\(curl.*nginx\.org/download.*\)%# mod_security\napt-get install -y libmodsecurity-dev modsecurity-crs\ncd /tmp\ngit clone --depth 1 https://github.com/SpiderLabs/ModSecurity-nginx.git\n\n\1%' install-nginx

# mettre à jour la ligne de configuration pour inclure le module ModSecurity récupéré ci-dessus
sed -i '/ModSecurity/! s%^[^#]*./configure \(.*nginx.*\)%#./configure \1\n./configure \1 --add-module=/tmp/ModSecurity-nginx%' install-nginx

# ajouter une ligne à la section de nettoyage
grep 'rm -fr /tmp/ModSecurity-nginx' install-nginx || sed -i 's%\(rm -fr.*/tmp/nginx.*\)%rm -fr /tmp/ModSecurity-nginx\n\1%' install-nginx

Notez que le Dockerfile responsable de l’exécution du script install-nginx est exécuté lors de la construction de l’image. L’image n’est construite que par l’équipe Discourse avant d’être téléchargée sur docker hub. Lorsque la commande Discourse ./launcher rebuild app est exécutée, elle déclenche (si des mises à jour sont disponibles) un docker pull, qui récupère la dernière image Docker Discourse depuis docker hub. Encore une fois, cela ne reconstruit pas l’image, n’exécute pas le Dockerfile, ni n’exécute le script install-nginx modifié ci-dessus.

La seule façon (que je connaisse) de déclencher l’exécution du script bash install-nginx mis à jour (qui est exécuté par le Dockerfile) est de faire construire une nouvelle image par Docker. Par exemple, cela déclenche Docker pour construire une nouvelle image nommée discourse_modsecurity – qui sera construite en utilisant le script install-nginx localement modifié :

docker build --tag 'discourse_modsecurity' /var/discourse/image/base/

Malheureusement, je n’ai pas trouvé de moyen de dire à launcher d’utiliser une image personnalisée (spécifier un run-image utilise l’image spécifiée directement, sans exécuter les modèles contre elle – comme nécessaire pour configurer réellement [par opposition à simplement installer] nginx). Nous remplaçons donc la variable image définie dans le script launcher pour utiliser notre nouvelle image Docker locale nommée discourse_modsecurity.

# remplacer la ligne "image="discourse/base:<version>" par 'image="discourse_modsecurity"'
grep 'discourse_modsecurity' launcher || sed --in-place=.`date "+%Y%m%d_%H%M%S"` '/base_image/! s%^\(\s*\)image=\(.*\)$%#\1image=\2\n\1image="discourse_modsecurity"%' /var/discourse/launcher

Maintenant, nous ajoutons un nouveau fichier de modèle pour configurer nos paramètres nginx afin d’inclure les fichiers/blocs modsecurity nécessaires.

cat << EOF > /var/discourse/templates/web.modsecurity.template.yml
run:
  - exec:
     cmd:
       - sudo apt-get install -y modsecurity-crs
       - cp /etc/modsecurity/modsecurity.conf-recommended /etc/modsecurity/modsecurity.conf
       - sed -i 's/SecRuleEngine DetectionOnly/SecRuleEngine On/' /etc/modsecurity/modsecurity.conf
       - sed -i 's^\(\s*\)[^#]*SecRequestBodyInMemoryLimit\(.*\)^\1#SecRequestBodyInMemoryLimit\2^' /etc/modsecurity/modsecurity.conf
       - sed -i '/nginx/! s%^\(\s*\)[^#]*SecAuditLog \(.*\)%#\1SecAuditLog \2\n\1SecAuditLog /var/log/nginx/modsec_audit.log%' /etc/modsecurity/modsecurity.conf

  - file:
     path: /etc/nginx/conf.d/modsecurity.include
     contents: |
        ################################################################################
        # File:    modsecurity.include
        # Version: 0.1
        # Purpose: Defines mod_security rules for the discourse vhost
        #          This should be included in the server{} blocks nginx vhosts.
        # Author:  Michael Altfield <michael@opensourceecology.org>
        # Created: 2019-11-12
        # Updated: 2019-11-12
        ################################################################################
        Include "/etc/modsecurity/modsecurity.conf"
        
        # OWASP Core Rule Set, installé depuis le paquet 'modsecurity-crs' dans debian
        Include /etc/modsecurity/crs/crs-setup.conf
        Include /usr/share/modsecurity-crs/rules/*.conf

  - replace:
     filename: "/etc/nginx/conf.d/discourse.conf"
     from: /server.+{/
     to: |
       server {
         modsecurity on;
         modsecurity_rules_file /etc/nginx/conf.d/modsecurity.include;

EOF

Et nous ajoutons ce modèle (templates/web.modsecurity.template.yml) au bloc templates du fichier de configuration YAML de notre application, de sorte que cela ressemble à quelque chose comme ceci :

[root@osestaging1 discourse]# vim /var/discourse/containers/app.yml
...
[root@osestaging1 discourse]# grep -A 6 'templates:' /var/discourse/containers/app.yml
templates:
  - "templates/postgres.template.yml"
  - "templates/redis.template.yml"
  - "templates/web.template.yml"
  - "templates/web.ratelimited.template.yml"
  - "templates/web.socketed.template.yml"
  - "templates/web.modsecurity.template.yml"
[root@osestaging1 discourse]# 

Maintenant, lorsque vous reconstruisez l’application Docker Discourse, elle utilisera votre nouvelle image Docker discourse_modsecurity avec nginx et modsecurity, et configurera nginx pour utiliser OWASP CRS.

/var/discourse/launcher rebuild app

Je suis tout à fait d’accord avec vous : ModSecurity ou des WAF similaires ne sont pas une solution miracle.

Cependant, je connais (du moins ?) une vulnérabilité découverte dans RoR qui a affecté Discourse qui aurait pu être atténuée par un mécanisme de type ModSecurity.

Lors de la correction de cette faille, nous avons constaté dans nos journaux qu’elle avait déjà été exploitée dans la nature sur au moins l’un de nos forums avant que la CVE ne soit rendue publique et corrigée. Cela n’a entraîné aucune divulgation d’informations, mais cela était dû uniquement au fait que nous avions mis en œuvre certaines choses différemment d’une installation standard (c’est-à-dire, de la chance).

Je ne suis pas certain que la complexité ajoutée l’emporte sur la sécurité supplémentaire apportée.

Je compare les WAF à des antivirus basés sur les signatures qui, selon moi, ne sont pas très utiles dans des environnements à surface d’attaque limitée (par exemple, vos serveurs non-Windows).

Ils ne détecteront pas tout, mais ils (en théorie) repéreront les schémas d’attaques courants (par exemple, les injections SQL) et les exploits connus (par exemple, la vulnérabilité RoR mentionnée ci-dessus) sur une gamme de logiciels.

Je vois l’intérêt de les déployer, surtout dans un environnement d’entreprise où vous avez une multitude d’applications exécutant n’importe quoi, et où vous devez savoir que chaque application est protégée contre ces exploits sans avoir à vous soucier de chacune individuellement — cela transforme un problème de taille NxM en un problème de taille N+M.

S’il vaut la peine de les exécuter sur votre site est une autre question, relevant de l’analyse risque (de dysfonctionnement) / bénéfice (de protection) que vous devrez mener vous-même.