Protéger Nginx des attaques DoS et bruteforce

Bloqueur de pub détectée - Vous bloquez l'affichage des publicités.
Pour soutenir le site, merci de bien vouloir laisser les publicités s'afficher
Plus d'informations : Comment désactiver les bloqueurs de publicité sur un site internet

Nginx est un serveur WEB de plus en plus prisé.
Parmi ces nombreuses fonctionnalités, il existe des parades et protections contre les attaques DoS et bruteforce.

Voici quelques réglages et configurations à appliquer pour améliorer la sécurité de votre serveur WEB.
Elles permettent de limiter certaines attaques DoS et surtout attaque bruteforce.

Introduction aux protections Nginx

Pour rappel, il existe de nombreuses techniques et attaques DoS différentes.
Parmi ces attaques, on trouve les attaques Layer 7 ou HTTP Flood.
Par layer 7, on parle de la couche 7 OSI.
Plus d'informations : Le protocole TCP/IP : définition, architecture et couches

Celle-ci consiste à envoyer des requêtes HTTP et si possibles sur celles qui demandes le plus de ressources systèmes.
Cela peut conduire à saturer le serveur WEB ou PHP si par exemple cela la dépasse la limite de worker ou le nombre maximum de connexion MySQL.

Nginx propose plusieurs protections contre ce type d'attaque qui peuvent être activé dans la configuration du serveur WEB.
Ces protections visent à limiter le nombre de requêtes ou de connexions concurrentes pour une même adresse IP.
Il s'agit de configurer un seuil (limite de taux) et si une machine la dépasse, la requête peut être rejetée.

Il faut être conscient qu'il s'agit de bloquer trop de requêtes provenant d'une même IP.
Ainsi selon la méthode d'attaques DoS, cette protection peut ne pas suffire.
En effet, si l'attaque a un pool d'IP important il lui suffit d'éviter de trop faire de connexion depuis une même IP et faire tourner un maximum les IP disponibles.
Ainsi une adresse IP reste en dessous du seuil de déclenchement mais l'attaque peut tout de même être conséquente.
Mais ça ne peut pas faire de mal et aider soit à mitiger des attaques, soit bloquer totalement certains bruteforce trop violents.

Voici une vidéo pour bien qui fait suivre quelques attaques DoS Layer 7 :

Enfin l'article vous guide pour mettre en place des blocages d'IP, referer ou sur le user agent.

Sécuriser Nginx pour le protéger des attaques DoS et bruteforce

Les limites de taux (rate limit)

limit_req_zone

Dans un premier temps, il faut créer une zone dans le contexte http à l'aide la directive limit_req_zone.
Voici la syntaxe de la directive limit_req_zone.

Syntax: 	limit_req_zone key zone=name:size rate=rate [sync];
Default: 	—
Context: 	http

Imaginons que vous souhaitez limiter le nombre de requêtes sur une adresse IP d'un client.
Voici la syntaxe que l'on peut utiliser

limit_req_zone $binary_remote_addr zone=flood:10m rate=5r/s;
  • $binary_remote_addr correspond aux adresses IP des clients qui se connectent à votre serveur WEB Nginx. Vous pouvez utiliser les variables $http_x_forwarded_for mais cela n'est pas recommandé.
  • zone est le nom de la zone que vous donnez à votre directive limit_req_zone, ici, flood
  • 10m correspond à la quantité de mémoire qui peuvent être utilisées pour stocker les adresses IP. 16,000 adresse IP utilisent environ 1 Mo, donc ici on peut en stocker 160 000.
  • enfin rate correspond au nombre de requêtes autorisées. Dans cet exemple 1 requête par seconde. On peut utiliser m pour les minutes.
Utilisez les zones à bon inscients, par exemple la page d'identification de WordPress ou commentaires doit être plus sensibles que les pages d'artcles de votre site.

limit_req

Ensuite, il faut utiliser la directive limit_req dans les blocs location ou server utilisant la zone précédemment créée.
La syntaxe est la suivante :

Syntax: 	limit_req zone=name [burst=number] [nodelay | delay=number];
Default: 	—
Context: 	http, server, location

Par exemple si on veut protéger une page login des attaques :

limit_req_zone $binary_remote_addr zone=flood:10m rate=5r/s;

location /login/ {
    limit_req zone=flood burst=12 delay=8;
    proxy_pass http://my_upstream;
}

Dans cet exemple, on charge la zone flood à 5 requêtes par seconde avec un burst à 12 et un delay de 8.
Cela signifie que l'on n'autorise que 4 requêtes.
Ainsi au delà des 8 requêtes, on impose un délai afin de ne pas dépasser la limite des 5 requêtes par seconde.
Si on ne souhaite pas imposer de délai, il faut utiliser le paramètre nodelay.

Les 8 premières demandes (la valeur de délai) sont envoyées sans délai par Nginx. Les 4 demandes suivantes (rafale - délai) sont retardées afin que le débit défini de 5 tr / s ne soit pas dépassé. Les 3 demandes suivantes sont rejetées car la taille totale de la rafale a été dépassée. Les demandes suivantes sont retardées.

Le paramètre delay se règle en fonction du nombre d'éléments chargés par page.
En général, pour la majorité des sites, il est autour de 12.

Enfin, lorsque le client dépasse la limite, une erreur 503 est renvoyée (paramétrable) ainsi que la génération de logs suivants.

11855#0: *5728 limiting requests, excess: 10.790 by zone "flood", client: xx.xx.xx.xx, server: forum.malekal.com, request: "GET /viewtopic.php?f=2 HTTP/1.0", host: "forum.malekal.com"

limit_conn conn_limit_per_ip

limit_conn conn_limit_per_ip est une autre directive qui peut-être déclaré dans les bloc server, location.
Celle-ci permet de limiter le nombre de connexion par IP.

Par exemple pour limiter le nombre de connexion par IP à 30 :

limit_conn conn_limit_per_ip 30

Au delà du nombre de connexions concurrentes autorisées alors Nginx retourne une erreur 503.

Définir le code HTTP sur la limite de taux

On peut ensuite définir le code HTTP qui sera renvoyée par le blocage des requêtes lors d'un dépassement du seuil.
Par exemple, on peut renvoyer un code HTTP 429 Too Many Requests.
Cela se fait avec limit_req_status.

limit_req_status 429;

Tester la protection et conclusion

Par exemple cette IP turque a tenté un bruteforce WordPress, comme il n'a pas défini de pause entre chaque tentative, il dépasse le seuil.
On voit alors que le code HTTP retourné est 429.

Nginx dépassement de seuil et taux qui bloque la connexion

Fail2ban : protection avec iptables

Fail2ban est un daemon présent sur la plupart des distributions qui peut être utilisé en plus des protections Nginx.
Ce daemon peut lire les logs Nginx pour détecter les erreur 503 et au bout d'un certains nombres d'erreur générer une règle iptables pour bloquer complètement l'adresse IP.

Enfin, les logs fail2ban se trouvent dans /var/log/fail2ban.log

Bloquer le referer

On peut être amené à vouloir bloquer les connexions HTTP avec certains referer (référant).
Voici comment faire sur nginx.

  • Créez le fichier /etc/nginx/blacklist-referer.conf
  • Puis inspirez-vous de ce contenu où ici les sites baidu, reddit et qq seront bloqués :
map $http_referer $bad_referer {
    hostnames;

    default                           0;

    # Put regexes for undesired referers here
        "~www.baidu.com"   1;
        "~www.reddit.com"    1;
        "~www.qq.com"    1;
}
  • Ensuite dans la directive http de nginx, il faut inclure le fichier de configuration. Si vous ne voulez pas que ce s'applique à tous les sites, le mettre dans la directive server.
include blacklist-referer.conf;
  • Enfin dans la directive server, on ajoute ce test qui retourne 403 pour les referrer à 1 dans le map.
if ($bad_referer) {
            return 403;
}

Il ne reste plus qu'à relancer nginx et tester.

Pour tester le referer avec curl, on utilise le paramètre -e :

curl -e www.sitereferer.tld https://www.monsite.tld

Bloquer des user agent

On peut faire la même chose avec les user agent.

  • Par exemple, on créé le fichier /etc/nginx/blacklist-UA.conf
map $http_user_agent $block_ua {
        default           0;
        ~*profound        1;
        ~*scrapyproject   1;
        ~*netcrawler      1;
        ~*nmap            1;
        ~*sqlmap          1;
        ~*slowhttptest    1;
        ~*nikto           1;
        ~*jersey          1;
        ~*brandwatch      1;
        ~*magpie-crawler  1;
        ~*mechanize       1;
        ~*python-requests 1;
        ~*redback         1;
        ~*python          1;
        ~*wget            1;
        ~*Pcore-HTTP      1;
        ~*HTTrack         1;
}
  • Ensuite dans la directive http de nginx, on inclut le fichier de configuration
include blacklist-UA.conf;
  • Enfin on ajoute le test :
if ($block_ua) {
                return 403;
        }
  • Enfin relancez nginx puis testez

Avec curl, pour tester le useragent, on utilise le paramètre -A en spécifiant ce dernier :

curl -A UA https://www.monsite.tld

Bloquer des adresses ou plages d'IP

Enfin il est tout à fait possible de mettre des IP en liste blanche ou listes noires.
Là aussi grâce à l'option map on peut faire des listes pour un test.

  • Par exemple créez le fichier /etc/nginx/blacklist-ip.conf
  • Ajoutez les IP à bannir en utilisant deny suivi de l'IP et un point virgule à chaque fin de ligne
deny 41.242.103.118;
deny 1.1.1.1;
deny 41.98.135.92;
deny 41.188.53.31;
deny 160.177.104.123;

Enfin on inclut le fichier de configuration dans la directive http :

include blacklist-ip.conf;

Protéger l'accès une ressource comme une page d'administration WordPress

Une des solutions pour protéger une ressources importantes contre les attaques par bruteforce est de configurer une protection par mot de passe ou par IP.
Cela est utile sur les pages d'administration comme celle de WordPress.

Par un filtre IP

Rien de bien compliquer, on inclut la liste des IP autorisés et on configure un deny sur le reste.

Créez le fichier /etc/nginx/allow.conf qui va lister les adresses IP autorisées grâce à allow :

allow 195.89.134.0/23;
allow 186.93.228.0/22;
allow 2.4.48.0/24;
allow 2.4.49.0/24

Puis par exemple pour protéger l'accès la page de login et d'identification de WordPress.
On inclut le fichier allow qui liste les IP autorisées puis on interdit le reste avec deny.

 location ~ ^/(wp-login.php|xmlrpc.php)$ {
                include /etc/nginx/allow.conf;
                deny    all;

                fastcgi_pass unix:/run/php/php7.3-fpm.sock;
                include fastcgi.conf;
                fastcgi_cache_bypass 1;
                fastcgi_no_cache 1;
                include fastcgi_params;
                fastcgi_keep_conn on;
        }

Par un mot de passe

Vous pouvez aussi protéger et bloquer l'accès par une demande de mot de passe.
C'est le même principe qu'avec le htaccess d'Apache.

On créé tout d'abord le fichier qui stocke le nom d'utilisateur et le mot de passe sous la forme d'un hachage.
Le programme htpasswd le fait pour vous sinon il existe des sites WEB.
On créé le dossier avec les bonnes permissions pour protéger le fichier.
Puis pour créer l'utilisateur Superadmin.

mkdir /etc/nginx/passwd
chmod 660 /etc/nginxpasswd
htpasswd /usr/local/etc/apache/.htpassword Superadmin

Ensuite saisissez un mot de passe sûr et fort, se reporter à cette page : Comment choisir « un bon » mot de passe fort et sécurisé

Enfin pour protéger la page d'administration de WordPress par un mot de passe, on utilise auth_basic_user_file comme ceci :

 location ~ ^/(wp-login.php|xmlrpc.php)$ {
                auth_basic_user_file /etc/nginx/passwd/.htpassword;

                fastcgi_pass unix:/run/php/php7.3-fpm.sock;
                include fastcgi.conf;
                fastcgi_cache_bypass 1;
                fastcgi_no_cache 1;
                include fastcgi_params;
                fastcgi_keep_conn on;
        }

Sécuriser Nginx et WordPress avec Cloudflare

Enfin vous pouvez grandement améliorer la sécurité de votre site WEB avec CloudFlare.
C'est un CDN qui agit en tant que proxy inversé avec des fonctionnalités de sécurité notamment avec un WAF.

Enfin l'article plus général pour sécuriser WordPress :

Liens


Vous avez trouvé cet article utile et interressant, n'hésitez pas à le partager...

Trouver la solution sur le forum d'aide

Vous êtes arrivé au terme de l'article Protéger Nginx des attaques DoS et bruteforce mais vous n'avez pas trouvé la solution à votre problème...
Suivez ces articles du forum pour trouver une réponse ou demandez à votre tour de l'aide sur le forum