Protéger son serveur Web Apache2 des attaques DoS

SYN Flood attaque

DoS m'a tué

Tous les serveurs connecté à Internet peuvent subir une attaque par déni de service ou Deny of Service ou DoS.

Une attaque DoS consiste à tenter de bloquer l'accès à un serveur en monopolisant toutes ses ressources. Certaines actions préventives permettent de limiter la casse.

Cet article vous présente quelques réglages de base pour vous protéger un serveur Linux Ubuntu des attaques DoS

Le principe d'une attaque DoS

Les ordinateurs ont une capacité de traitement limitée dans un temps donné. Si le nombre de requêtes excède la limite prévue, les nouvelles requêtes sont mises en attente. Le service au utilisateur est dégradé.
Si la file d'attente sature à son tour, les nouvelles requêtes sont ignorées au mieux. Le service est interrompu momentanément.
Au pire sur un serveur mal configuré, les services secondaires, comme un serveur Web, utilisent des ressources nécessaires au bon fonctionnement du serveur, comme son noyau. L'intégrité du serveur est alors compromise et celui-ci peut se bloquer. Le service est interrompu jusqu'à une intervention manuelle de l'administrateur.

Les attaques DoS les plus connues sont :

  • SYN flood qui consiste à initier une transaction TCP avec un serveur sans l'informer qu'elle est établie. Le serveur réserve les ressources en attendant la réponse.
  • ping flood qui consiste à saturer la bande passante d'un serveur en l'innondant de ping
  • attaque de service sur un port donné ; par exemple, le port 80 pour un serveur Web.

Les outils ab et hping peuvent simuler une petite attaque DoS sur un serveur Web, mais en aucun cas ils ne sont représentatifs d'une vraie attaque dans leur version originale de code source.

Des contremesures sont disponibles pour chaque type d'attaque. Cependant, il est impossible de protéger complètement ses routeurs, sa bande passante et ses serveurs face à une attaque bien préparée.

Linux contre les attaques DoS

Les attaques du type SYN flood peuvent être contrées au niveau du noyau Linux.
Pour vérifier si vous êtes victime d'une attaque DoS, utilisez l'outil netstat :

# netstat -an | grep SYN

Si vous observez plusieurs dizaines de connexion du type SYN_RECV, vous êtes victime d'un SYN flood.

Pour vous protéger au niveau du noyau Linux, éditez le fichier /etc/sysctl.conf et ajoutez les lignes :

# vérifie que l'origine de la requête, limite le spoofing IP
net.ipv4.conf.all.rp_filter = 1
# 1024 connexions non confirmées max, limite le SYN flood
net.ipv4.tcp_max_syn_backlog = 1024 

Redémarrez le service :

# sysctl -p /etc/sysctl.conf

Le Firewall contre les attaques DoS

Contre les attaques DoS de haut niveau, l'usage d'un firewall est le plus efficace. Le programme iptables est beaucoup moins gourmand en CPU et en mémoire qu'un process apache2 et repousse donc les limites de tolérance d'une attaque DoS pour une machine donnée.

Il permet également un réglage plus fin. Par exemple, iptables permet de limiter le nombre de connexions simultanées provenant d'une adresse IP ou d'un réseau, le nombre de connexions depuis une source, le tout sur une période donnée.
Les règles suivantes permettent de limiter le nombre de connexion depuis une source à 50 hits pendant 10 secondes sur le port 80, paramètre à ajuster en fonction de vos besoins :

# iptables -A INPUT -i eth0 -p tcp --dport 80 -m state --state NEW -m recent --set --name WEB
# iptables -A INPUT -i eth0 -p tcp --dport 80 -m state --state NEW -m recent \
--update --seconds 10 --hitcount 50 --rttl --name WEB -j DROP

Cela évite les attaques DoS simples mais reste peu efficace face aux attaques massivement distribuées. On parle alors d'attaque par déni de service distribuée ou de Distributed Denial of Service attacks ou DDoS.
Des centaines voir des milliers d'ordinateurs, souvent infectés par un virus, sont alors utilisés pour envoyer simultanément des requêtes à votre serveur. Dans ce cas, la capacité du hardware de votre infrastructure, routeur et serveur, à traiter les requêtes est prédominante.

Apache contre les attaques DoS

Le serveur Apache peut être configuré pour limiter les impacts d'une attaque DoS.
Ouvrez une console et éditez le fichier /etc/apache2/apache2.conf :

  • La plus importante, ajustez la directive MaxClients pour interdire au serveur Web d'utiliser toutes les ressources de la machine. Pour se faire, divisez la mémoire vive que vous voulez allouer à Apache par la taille moyenne de mémoire virtuelle utilisée d'un process Apache2, visible avec la commande top. Vous obtenez le nombre maximum de client avant que la machine utilise le swap.
  • Diminuez la directive TimeOut autant que votre application Web le permet. Par exemple, la valeur 60 libérera la connexion au bout de 60 secondes en envoyant un timeout au client. Par conséquent les traitements normaux ne devront également pas excéder 60 secondes. Attention au long traitement batch.
  • Diminuez la directive KeepAliveTimeout. Attention, une valeur trop basse impacte les performances.
  • Vous pouvez limiter la taille des requêtes, des headers, des champs HTTP avec les directives LimitRequestBody, LimitRequestFields, LimitRequestFieldSize, LimitRequestLine, et LimitXMLRequestBody. A utiliser avec prudence pour ne pas bloquer une fonctionnalité.

mod_evasive contre les attaques DoS

Vous pouvez également installer un module Apache dédié à la prévention des attaques DoS.
Le mod_evasive crée une table dynamique des IP clients et des URL demandées. Par défaut, il blacklist et renvoie une erreur 403 si l'IP :

  • fait plus de X requêtes par seconde sur une uri
  • fait plus de X requêtes par seconde sur un process Apache
  • continue de faire des requêtes alors qu'il est blacklisté

Pour l'installer et activer, ouvrez une ligne de commande :

# aptitude install libapache2-mod-evasive
# a2enmod mod-evasive

Pour définir la configuration :

# vi /etc/apache2/mods-available/mod-evasive.conf

Ajoutez et ajustez à vos besoins les lignes suivantes :

# mod_evasive

<IfModule mod_evasive20.c>
           # taille de la table de surveillance, a augmenter pour les DDoS
           DOSHashTableSize 2048
           # Nb maximum de refresh d'une uri par periode
           DOSPageCount 5
           # Duree de la periode pour la uri
           DOSPageInterval 1
           # Nb maximum de requete sur le site par periode
           DOSSiteCount 150
           # Duree de la periode pour le site
           DOSSiteInterval 1
           # Duree du blacklistage de l'IP reconduite si retentative
           DOSBlockingPeriod 10
           # Execute une action quand une IP est bloquee
           DOSSystemCommand "/sbin/iptables -I INPUT -s %s -j DROP"
           # envoie de mail quant une IP est bloquee
           DOSEmailNotify "admin@localhost"
           # repertoire des logs
           DOSLogDir "/var/log/apache2/"
           # Whitelist non surveillee
           DOSWhiteList 127.0.0.1
           DOSWhitelist 192.168.0.*
</IfModule>

Redémarrez Apache :

# /etc/init.d/apache2 restart

Vous pouvez maintenant tester le résultat. Tout d'abord un test depuis le réseau local dans la whitelist, il ne doit y avoir aucune erreur :

# ab -n 1000 -c 100 http://localhost/
This is ApacheBench, Version 2.3 <$Revision: 655654 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking www.webstrat.fr (be patient).....done


Server Software:        Apache/2.2.11
Server Hostname:        localhost
Server Port:            80

Document Path:          /
Document Length:        27229 bytes

Concurrency Level:      100
Time taken for tests:   0.146 seconds
Complete requests:      1000
Failed requests:        0
...

Le même test depuis une machine externe :

# ab -n 1000 -c 100 http://mon-ip-publique/
This is ApacheBench, Version 2.3 <$Revision: 655654 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking www.webstrat.fr (be patient).....done

Server Software:        Apache/2.2.11
Server Hostname:        mon-ip-publique
Server Port:            80

Document Path:          /
Document Length:        27229 bytes

Concurrency Level:      100
Time taken for tests:   4.614 seconds
Complete requests:      1000
Failed requests:        832
...

83% des requêtes ont été bloquées. L'attaque DoS va nécessité plus d'effort pour mettre à genou votre serveur.

Commentaires

Bonjour,

Merci pour cet article. Est-ce qu’il manque quelque chose à la fin de la première règle IPTABLES que vous mentionnez :

iptables -A INPUT -i eth0 -p tcp --dport 80 -m state --state NEW -m recent --set --name WEB ?

Car j’ai un message d’erreur. Faut-il ajouter ACCEPT à la fin de la ligne ?

Merci.

Bonjour, J'ai ceci qui s'affiche: apr_socket_recv: Connection timed out (110) Pouvez-vous m'aider ? Cdt

Vous avez atteint le nombre limite de socket réseau de votre serveur. AB n'en a plus pour effectuer les connexions demandées.

Il vous faut augmenter le nombre de socket sur votre serveur web ou sur vos serveurs d'injection pour solliciter plus loin votre serveur Web. Cordialement

Bonjour, J'ai ceci qui s'affiche: apr_socket_recv: Connection timed out (110) Pouvez-vous m'aider ? Cdt

La ligne de commande est complète.

Il est possible que votre interface réseau ne soit pas eth0 ou que votre version de iptables soit ancienne ou spécial (machine virtuelle) et ne supporte pas la définition d'état : --state.

Mod_evasive me semble intéressant mais je lui trouve une limite fort gênante.

Prenons l'exemple d'un site PHP/MySQL classique, dont les pages contiennent en moyenne 50 images :

- un utilisateur qui charge une page dans son navigateur charge la page http://exemple.com/toto.php et le navigateur récupère aussi les 50 images. Ca fait 51 requêtes de cette IP en l'espace d'1 seconde. Si l'utilisateur (ou quelques personnes derrière une même IP) consulte 5 pages en 10s il fait donc plus de 500 requêtes en 10s que le serveur encaisse facilement.

- un bot, à l'inverse, ne récupère pas les images... Mais s'il fait 500 requêtes en 10s vers le script PHP mon serveur va souffrir et j'aimerais limiter ça.

Le problème est que mod_evasive ne permet pas (à ma connaissance) de différencier les requêtes vers des images des requêtes vers des scripts PHP. Peut-on envisager de n'appliquer les limitations de mod_evasive qu'aux scripts PHP et non aux images ?

Bonjour,
mod_evasive permet bien la distinction entre hit sur le server et hit sur une ressource. Le terme Page est ici un abus de language. Il correspond à l'URI d'une ressource : page, image, css...

Dans mon exemple, pour une IP source donnée, bot ou personne physique, j'autorise :

  • 150 hits tout confondu sur le serveur par seconde (paramètres DOSSiteCount et DOSSiteInterval)
  • 5 hits par URI par seconde (paramètres DOSPageCount et DOSPageInterval)

Si je reprends ton exemple, cette configuration va autoriser le bot à faire 5 hits par seconde sur un script PHP donné, soit 50 toutes les 10 secondes.
Au delà de cette valeur, le bot sera bloqué pour une durée définie.

Bon billet. A noter qu'il est possible d'utiliser un reverse-proxy en frontal pour éviter que le serveur réel n'encaisse tout ça... (seul le reverse proxy prendra, et il est plus facile de le relancer via un cron qui l'inspecte régulièrement par exemple).

Le plus gros problème sont les attaques DoS zombies : un réseau de botnet tente une connexion à votre serveur avec une connexion par IP. Impossible de dissocier le traffic normal de l'attaque. Il suffit de voir que même twitter a du mal à s'en protéger pour comprendre que ce n'est pas une mince affaire de s'en débarrasser...

Merci pour le retour.

Le proxy a en effet sa place entre le firewall et le serveur Web. Il sera au pire un buffer qui temporise le flux de requêtes, au mieux un filtre efficace.
J'aborderai dans un autre billet la mise en place du proxy varnish et en profiterai pour faire un point pratique sur l'optimisation de la performance d'un site Web.

Petite amélioration :

A la place du :

vi /etc/apache2/mods-enabled/mod-evasive.conf

Faire :

vi /etc/apache2/mods-available/mod-evasive.conf

ln -s /etc/apache2/mods-available/mod-evasive.conf /etc/apache2/mods-enabled/mod-evasive.conf