DDoS à la rescousse : comment nous effectuons des tests de contrainte et de charge

DDoS à la rescousse : comment nous effectuons des tests de contrainte et de charge

Variti développe une protection contre les robots et les attaques DDoS, et effectue également des tests de stress et de charge. Lors de la conférence HighLoad++ 2018, nous avons expliqué comment sécuriser les ressources contre différents types d'attaques. En bref : isolez certaines parties du système, utilisez les services cloud et les CDN et mettez à jour régulièrement. Mais vous ne pourrez toujours pas assurer votre protection sans entreprises spécialisées :)

Avant de lire le texte, vous pouvez lire les courts résumés sur le site de la conférence.
Et si vous n’aimez pas lire ou souhaitez simplement regarder la vidéo, l’enregistrement de notre reportage est ci-dessous sous le spoiler.

Enregistrement vidéo du rapport

De nombreuses entreprises savent déjà comment effectuer des tests de charge, mais toutes ne réalisent pas de tests de résistance. Certains de nos clients pensent que leur site est invulnérable car ils disposent d'un système à forte charge et qu'il protège bien des attaques. Nous montrons que cela n’est pas entièrement vrai.
Bien entendu, avant d'effectuer des tests, nous obtenons l'autorisation du client, signée et tamponnée, et avec notre aide, une attaque DDoS ne peut être menée contre personne. Les tests sont effectués à un moment choisi par le client, lorsque le trafic vers sa ressource est minime et que les problèmes d'accès n'affecteront pas les clients. De plus, comme quelque chose peut toujours mal se passer pendant le processus de test, nous sommes en contact permanent avec le client. Cela vous permet non seulement de signaler les résultats obtenus, mais également de modifier quelque chose pendant les tests. À la fin des tests, nous rédigeons toujours un rapport dans lequel nous signalons les lacunes détectées et donnons des recommandations pour éliminer les faiblesses du site.

Comment nous travaillons

Lors des tests, nous émulons un botnet. Étant donné que nous travaillons avec des clients qui ne se trouvent pas sur nos réseaux, afin de garantir que le test ne se termine pas dans la première minute en raison du déclenchement de limites ou de protections, nous fournissons la charge non pas à partir d'une IP, mais à partir de notre propre sous-réseau. De plus, pour créer une charge importante, nous disposons de notre propre serveur de test assez puissant.

Postulats

Trop ne veut pas dire bien
Moins nous pouvons faire échouer une ressource, mieux c'est. Si vous pouvez empêcher le site de fonctionner avec une requête par seconde, voire une requête par minute, c'est génial. Car selon la loi de la méchanceté, les utilisateurs ou les attaquants tomberont accidentellement dans cette vulnérabilité particulière.

Mieux vaut un échec partiel qu’un échec complet
Nous conseillons toujours de rendre les systèmes hétérogènes. De plus, il vaut la peine de les séparer au niveau physique, et pas seulement par conteneurisation. En cas de séparation physique, même si quelque chose tombe en panne sur le site, il est fort probable qu'il ne cessera pas complètement de fonctionner et que les utilisateurs continueront d'avoir accès à au moins une partie des fonctionnalités.

Une bonne architecture est la base de la durabilité
La tolérance aux pannes d'une ressource et sa capacité à résister aux attaques et aux charges doivent être définies dès la conception, en fait dès l'élaboration des premiers organigrammes dans un cahier. Car si des erreurs fatales s’insinuent, il est possible de les corriger à l’avenir, mais c’est très difficile.

Non seulement le code doit être bon, mais aussi la configuration
Beaucoup de gens pensent qu’une bonne équipe de développement est la garantie d’un service tolérant aux pannes. Une bonne équipe de développement est vraiment nécessaire, mais il doit aussi y avoir de bonnes opérations, un bon DevOps. Autrement dit, nous avons besoin de spécialistes qui configureront correctement Linux et le réseau, écriront correctement les configurations dans nginx, fixeront les limites, etc. Sinon, la ressource ne fonctionnera bien qu'en test, et à un moment donné, tout s'arrêtera en production.

Différences entre les tests de charge et de stress
Les tests de charge vous permettent d'identifier les limites du fonctionnement du système. Les tests de résistance visent à détecter les faiblesses d'un système et sont utilisés pour briser ce système et voir comment il se comportera en cas de défaillance de certaines parties. Dans ce cas, la nature de la charge reste généralement inconnue du client avant le début des tests de résistance.

Particularités des attaques L7

Nous divisons généralement les types de charges en charges aux niveaux L7 et L3&4. L7 est une charge au niveau de l'application, le plus souvent cela signifie uniquement HTTP, mais nous entendons toute charge au niveau du protocole TCP.
Les attaques L7 présentent certaines caractéristiques distinctives. Premièrement, ils arrivent directement dans l'application, c'est-à-dire qu'il est peu probable qu'ils soient reflétés via le réseau. De telles attaques utilisent la logique et, de ce fait, consomment le processeur, la mémoire, le disque, la base de données et d’autres ressources de manière très efficace et avec peu de trafic.

Inondation HTTP

Dans le cas de toute attaque, la charge est plus facile à créer qu’à gérer, et dans le cas de L7, cela est également vrai. Il n'est pas toujours facile de distinguer le trafic d'attaque du trafic légitime, et le plus souvent cela peut se faire par fréquence, mais si tout est correctement planifié, il est alors impossible de comprendre à partir des journaux où se trouve l'attaque et où se trouvent les demandes légitimes.
Comme premier exemple, considérons une attaque HTTP Flood. Le graphique montre que de telles attaques sont généralement très puissantes : dans l'exemple ci-dessous, le nombre maximal de requêtes dépassait 600 XNUMX par minute.

DDoS à la rescousse : comment nous effectuons des tests de contrainte et de charge

HTTP Flood est le moyen le plus simple de créer une charge. En règle générale, il faut une sorte d'outil de test de charge, tel qu'ApacheBench, et définit une requête et une cible. Avec une approche aussi simple, il existe une forte probabilité de tomber sur la mise en cache du serveur, mais il est facile de la contourner. Par exemple, ajouter des chaînes aléatoires à la requête, ce qui obligera le serveur à constamment proposer une nouvelle page.
N'oubliez pas non plus l'agent utilisateur en train de créer une charge. De nombreux agents utilisateurs d'outils de test populaires sont filtrés par les administrateurs système et, dans ce cas, la charge peut tout simplement ne pas atteindre le backend. Vous pouvez améliorer considérablement le résultat en insérant un en-tête plus ou moins valide du navigateur dans la requête.
Aussi simples que soient les attaques HTTP Flood, elles ont aussi leurs inconvénients. Premièrement, de grandes quantités d’énergie sont nécessaires pour créer la charge. Deuxièmement, de telles attaques sont très faciles à détecter, surtout si elles proviennent d’une seule adresse. En conséquence, les demandes commencent immédiatement à être filtrées soit par les administrateurs système, soit même au niveau du fournisseur.

Que rechercher

Pour réduire le nombre de requêtes par seconde sans perdre en efficacité, il faut faire preuve d'un peu d'imagination et explorer le site. Ainsi, vous pouvez charger non seulement un canal ou un serveur, mais également des parties individuelles de l'application, par exemple des bases de données ou des systèmes de fichiers. Vous pouvez également rechercher des endroits sur le site qui font de gros calculs : calculatrices, pages de sélection de produits, etc. Enfin, il arrive souvent que le site dispose d'une sorte de script PHP qui génère une page de plusieurs centaines de milliers de lignes. Un tel script charge également considérablement le serveur et peut devenir la cible d'une attaque.

Où chercher

Lorsque nous analysons une ressource avant de la tester, nous examinons d’abord, bien entendu, le site lui-même. Nous recherchons toutes sortes de champs de saisie, de fichiers lourds - en général, tout ce qui peut créer des problèmes pour la ressource et ralentir son fonctionnement. Les outils de développement banals de Google Chrome et Firefox aident ici, affichant les temps de réponse des pages.
Nous analysons également les sous-domaines. Par exemple, il existe une certaine boutique en ligne, abc.com, et elle possède un sous-domaine admin.abc.com. Il s'agit très probablement d'un panneau d'administration avec autorisation, mais si vous y mettez une charge, cela peut créer des problèmes pour la ressource principale.
Le site peut avoir un sous-domaine api.abc.com. Il s'agit très probablement d'une ressource pour les applications mobiles. L'application peut être trouvée dans l'App Store ou Google Play, installer un point d'accès spécial, décortiquer l'API et enregistrer des comptes de test. Le problème est que les gens pensent souvent que tout ce qui est protégé par autorisation est immunisé contre les attaques par déni de service. On suppose que l’autorisation est le meilleur CAPTCHA, mais ce n’est pas le cas. Il est facile de créer 10 à 20 comptes de test, mais en les créant, nous avons accès à des fonctionnalités complexes et non dissimulées.
Naturellement, nous examinons l'historique, robots.txt et WebArchive, ViewDNS, et recherchons les anciennes versions de la ressource. Parfois, il arrive que les développeurs aient déployé, par exemple, mail2.yandex.net, mais l'ancienne version, mail.yandex.net, demeure. Ce mail.yandex.net n'est plus pris en charge, les ressources de développement ne lui sont pas allouées, mais il continue de consommer la base de données. En conséquence, en utilisant l'ancienne version, vous pouvez utiliser efficacement les ressources du backend et tout ce qui se cache derrière la mise en page. Bien sûr, cela n’arrive pas toujours, mais nous le rencontrons encore assez souvent.
Bien entendu, nous analysons tous les paramètres de la demande et la structure des cookies. Vous pouvez, par exemple, transférer une certaine valeur dans un tableau JSON à l'intérieur d'un cookie, créer de nombreuses imbrications et faire fonctionner la ressource pendant une période déraisonnablement longue.

Chargement de recherche

La première chose qui vient à l'esprit lors de la recherche d'un site est de charger la base de données, car presque tout le monde a une recherche, et pour presque tout le monde, malheureusement, elle est mal protégée. Pour une raison quelconque, les développeurs n'accordent pas suffisamment d'attention à la recherche. Mais il y a une recommandation ici : vous ne devez pas faire de requêtes du même type, car vous pourriez rencontrer une mise en cache, comme c'est le cas avec HTTP Flood.
Faire des requêtes aléatoires sur la base de données n’est pas non plus toujours efficace. Il est préférable de créer une liste de mots-clés pertinents pour la recherche. Si l'on revient à l'exemple d'une boutique en ligne : disons que le site vend des pneus de voiture et permet de définir le rayon des pneus, le type de voiture et d'autres paramètres. En conséquence, les combinaisons de mots pertinents obligeront la base de données à fonctionner dans des conditions beaucoup plus complexes.
De plus, cela vaut la peine d'utiliser la pagination : il est beaucoup plus difficile pour une recherche de renvoyer l'avant-dernière page des résultats de recherche que la première. Autrement dit, avec l'aide de la pagination, vous pouvez légèrement diversifier la charge.
L'exemple ci-dessous montre la charge de recherche. On constate que dès la première seconde du test à une vitesse de dix requêtes par seconde, le site est tombé en panne et n'a pas répondu.

DDoS à la rescousse : comment nous effectuons des tests de contrainte et de charge

S'il n'y a pas de recherche ?

S'il n'y a pas de recherche, cela ne signifie pas que le site ne contient pas d'autres champs de saisie vulnérables. Ce champ peut être une autorisation. De nos jours, les développeurs aiment créer des hachages complexes pour protéger la base de données de connexion contre une attaque par table arc-en-ciel. C'est bien, mais de tels hachages consomment beaucoup de ressources CPU. Un flux important de fausses autorisations entraîne une panne du processeur et, par conséquent, le site cesse de fonctionner.
La présence sur le site de toutes sortes de formulaires de commentaires et de réactions est une raison pour y envoyer de très gros textes ou simplement créer un flot massif. Parfois, les sites acceptent les fichiers joints, notamment au format gzip. Dans ce cas, nous prenons un fichier de 1 To, le compressons en plusieurs octets ou kilo-octets à l'aide de gzip et l'envoyons au site. Ensuite, il est décompressé et un effet très intéressant est obtenu.

API REST

Je voudrais accorder un peu d'attention à des services aussi populaires que l'API Rest. Sécuriser une API Rest est beaucoup plus difficile qu'un site Web classique. Même les méthodes triviales de protection contre la force brute des mots de passe et autres activités illégitimes ne fonctionnent pas pour l'API Rest.
L'API Rest est très facile à casser car elle accède directement à la base de données. Dans le même temps, l’échec d’un tel service entraîne des conséquences assez graves pour les entreprises. Le fait est que l'API Rest est généralement utilisée non seulement pour le site Web principal, mais également pour l'application mobile et certaines ressources internes de l'entreprise. Et si tout cela tombe, alors l’effet est bien plus fort que dans le cas d’une simple panne de site Web.

Chargement de contenu lourd

Si l’on nous propose de tester une application ordinaire d’une seule page, une page de destination ou un site Web de carte de visite qui n’a pas de fonctionnalités complexes, nous recherchons un contenu lourd. Par exemple, les grandes images envoyées par le serveur, les fichiers binaires, la documentation PDF - nous essayons de télécharger tout cela. De tels tests chargent bien le système de fichiers et obstruent les canaux, et sont donc efficaces. Autrement dit, même si vous n'arrêtez pas le serveur, en téléchargeant un fichier volumineux à faible vitesse, vous obstruerez simplement le canal du serveur cible, puis un déni de service se produira.
Un exemple d'un tel test montre qu'à une vitesse de 30 RPS, le site a cessé de répondre ou a généré la 500e erreur de serveur.

DDoS à la rescousse : comment nous effectuons des tests de contrainte et de charge

N'oubliez pas de configurer les serveurs. Vous pouvez souvent constater qu'une personne a acheté une machine virtuelle, y a installé Apache, a tout configuré par défaut, a installé une application PHP, et ci-dessous vous pouvez voir le résultat.

DDoS à la rescousse : comment nous effectuons des tests de contrainte et de charge

Ici, la charge est allée à la racine et ne s'élevait qu'à 10 RPS. Nous avons attendu 5 minutes et le serveur est tombé en panne. Il est vrai qu’on ne sait pas exactement pourquoi il est tombé, mais on suppose qu’il avait simplement trop de mémoire et qu’il a donc cessé de répondre.

Basé sur les vagues

Au cours des deux dernières années, les attaques par vagues sont devenues très populaires. Cela est dû au fait que de nombreuses organisations achètent certains éléments matériels pour la protection DDoS, qui nécessitent un certain temps pour accumuler des statistiques et commencer à filtrer l'attaque. Autrement dit, ils ne filtrent pas l'attaque dans les 30 à 40 premières secondes, car ils accumulent des données et apprennent. En conséquence, au cours de ces 30 à 40 secondes, vous pouvez lancer tellement de choses sur le site que la ressource restera longtemps jusqu'à ce que toutes les demandes soient résolues.
Dans le cas de l’attaque ci-dessous, il y a eu un intervalle de 10 minutes, après quoi une nouvelle partie modifiée de l’attaque est arrivée.

DDoS à la rescousse : comment nous effectuons des tests de contrainte et de charge

Autrement dit, la défense a appris, a commencé à filtrer, mais une nouvelle partie de l’attaque, complètement différente, est arrivée et la défense a recommencé à apprendre. En effet, le filtrage cesse de fonctionner, la protection devient inefficace et le site est indisponible.
Les attaques par vagues se caractérisent par des valeurs très élevées au sommet, elles peuvent atteindre cent mille ou un million de requêtes par seconde, dans le cas de L7. Si nous parlons de L3&4, alors il peut y avoir des centaines de gigabits de trafic, ou, par conséquent, des centaines de mpps, si vous comptez en paquets.
Le problème de ces attaques est la synchronisation. Les attaques proviennent d’un botnet et nécessitent un haut degré de synchronisation pour créer un pic ponctuel très important. Et cette coordination ne fonctionne pas toujours : parfois, le résultat est une sorte de pic parabolique, qui semble plutôt pathétique.

Pas HTTP seul

En plus du HTTP en L7, nous aimons exploiter d'autres protocoles. En règle générale, un site Web classique, en particulier un hébergement standard, présente des protocoles de messagerie et MySQL qui ressortent. Les protocoles de messagerie sont soumis à moins de charge que les bases de données, mais ils peuvent également être chargés de manière assez efficace et se retrouver avec un processeur surchargé sur le serveur.
Nous avons réussi à utiliser la vulnérabilité SSH de 2016. Cette vulnérabilité a désormais été corrigée pour presque tout le monde, mais cela ne signifie pas que la charge ne peut pas être soumise à SSH. Peut. Il y a tout simplement une énorme charge d'autorisations, SSH consomme presque tout le processeur du serveur, puis le site Web s'effondre à partir d'une ou deux requêtes par seconde. Par conséquent, ces une ou deux requêtes basées sur les journaux ne peuvent pas être distinguées d’un chargement légitime.
De nombreuses connexions que nous ouvrons sur les serveurs restent également pertinentes. Auparavant, Apache en était coupable, maintenant nginx en est réellement coupable, car il est souvent configuré par défaut. Le nombre de connexions que nginx peut garder ouverte est limité, donc on ouvre ce nombre de connexions, nginx n'accepte plus de nouvelle connexion, et du coup le site ne fonctionne pas.
Notre cluster de test dispose de suffisamment de processeur pour attaquer la négociation SSL. En principe, comme le montre la pratique, les botnets aiment parfois faire cela aussi. D’une part, il est clair que vous ne pouvez pas vous passer de SSL, à cause des résultats Google, du classement, de la sécurité. En revanche, SSL a malheureusement un problème de CPU.

L3&4

Lorsque nous parlons d’une attaque aux niveaux L3&4, nous parlons généralement d’une attaque au niveau lien. Une telle charge se distingue presque toujours d’une charge légitime, à moins qu’il ne s’agisse d’une attaque SYN-flood. Le problème des attaques SYN-flood contre les outils de sécurité est leur volume important. La valeur maximale L3&4 était de 1,5 à 2 Tbit/s. Ce type de trafic est très difficile à traiter, même pour les grandes entreprises, notamment Oracle et Google.
SYN et SYN-ACK sont des paquets utilisés lors de l'établissement d'une connexion. Par conséquent, SYN-flood est difficile à distinguer d'une charge légitime : il n'est pas clair s'il s'agit d'un SYN venu établir une connexion, ou d'une partie d'un Flood.

Inondation UDP

En règle générale, les attaquants ne disposent pas des capacités dont nous disposons. L’amplification peut donc être utilisée pour organiser des attaques. Autrement dit, l'attaquant analyse Internet et trouve des serveurs vulnérables ou mal configurés qui, par exemple, en réponse à un paquet SYN, répondent par trois SYN-ACK. En usurpant l'adresse source à partir de l'adresse du serveur cible, il est possible d'augmenter la puissance, par exemple, trois fois avec un seul paquet et de rediriger le trafic vers la victime.

DDoS à la rescousse : comment nous effectuons des tests de contrainte et de charge

Le problème des amplifications est qu’elles sont difficiles à détecter. Des exemples récents incluent le cas sensationnel du memcached vulnérable. De plus, il existe désormais de nombreux appareils IoT, des caméras IP, qui sont également pour la plupart configurés par défaut, et par défaut, ils sont configurés de manière incorrecte, c'est pourquoi les attaquants effectuent le plus souvent des attaques via de tels appareils.

DDoS à la rescousse : comment nous effectuons des tests de contrainte et de charge

Inondation SYN difficile

SYN-flood est probablement le type d'attaque le plus intéressant du point de vue d'un développeur. Le problème est que les administrateurs système utilisent souvent le blocage IP pour se protéger. De plus, le blocage IP affecte non seulement les administrateurs système qui agissent à l'aide de scripts, mais aussi, malheureusement, certains systèmes de sécurité achetés très cher.
Cette méthode peut tourner au désastre, car si les attaquants remplacent les adresses IP, l'entreprise bloquera son propre sous-réseau. Lorsque le pare-feu bloque son propre cluster, la sortie échouera dans les interactions externes et la ressource échouera.
De plus, il n’est pas difficile de bloquer son propre réseau. Si le bureau du client dispose d'un réseau Wi-Fi ou si les performances des ressources sont mesurées à l'aide de divers systèmes de surveillance, nous prenons l'adresse IP de ce système de surveillance ou du Wi-Fi du bureau du client et l'utilisons comme source. Au final, la ressource semble être disponible, mais les adresses IP cibles sont bloquées. Ainsi, le réseau Wi-Fi de la conférence HighLoad, où est présenté le nouveau produit de l’entreprise, peut être bloqué, ce qui entraîne certains coûts commerciaux et économiques.
Pendant les tests, nous ne pouvons pas utiliser l'amplification via Memcached avec des ressources externes, car il existe des accords pour envoyer du trafic uniquement aux adresses IP autorisées. En conséquence, nous utilisons l'amplification via SYN et SYN-ACK, lorsque le système répond à l'envoi d'un SYN avec deux ou trois SYN-ACK, et à la sortie l'attaque est multipliée par deux ou trois.

Outils

L'un des principaux outils que nous utilisons pour la charge de travail L7 est Yandex-tank. En particulier, un fantôme est utilisé comme arme à feu, et il existe plusieurs scripts pour générer des cartouches et analyser les résultats.
Tcpdump est utilisé pour analyser le trafic réseau et Nmap est utilisé pour analyser le serveur. Pour créer la charge au niveau L3&4, OpenSSL et un peu de notre propre magie avec la bibliothèque DPDK sont utilisés. DPDK est une bibliothèque d'Intel qui vous permet de travailler avec l'interface réseau en contournant la pile Linux, augmentant ainsi l'efficacité. Naturellement, nous utilisons DPDK non seulement au niveau L3&4, mais également au niveau L7, car il nous permet de créer un flux de charge très élevé, de l'ordre de plusieurs millions de requêtes par seconde à partir d'une seule machine.
Nous utilisons également certains générateurs de trafic et outils spéciaux que nous écrivons pour des tests spécifiques. Si l’on rappelle la vulnérabilité sous SSH, alors l’ensemble ci-dessus ne peut être exploité. Si nous attaquons le protocole de messagerie, nous prenons les utilitaires de messagerie ou écrivons simplement des scripts dessus.

résultats

En conclusion, je voudrais dire :

  • En plus des tests de charge classiques, il est nécessaire de réaliser des tests de contrainte. Nous avons un exemple concret où le sous-traitant d'un partenaire a uniquement effectué des tests de charge. Cela a montré que la ressource peut supporter une charge normale. Mais ensuite une charge anormale est apparue, les visiteurs du site ont commencé à utiliser la ressource un peu différemment, et par conséquent le sous-traitant s'est retiré. Ainsi, cela vaut la peine de rechercher les vulnérabilités même si vous êtes déjà protégé contre les attaques DDoS.
  • Il est nécessaire d’isoler certaines parties du système des autres. Si vous effectuez une recherche, vous devez la déplacer vers des machines distinctes, c'est-à-dire même pas vers Docker. Car si la recherche ou l’autorisation échoue, au moins quelque chose continuera à fonctionner. Dans le cas d'une boutique en ligne, les utilisateurs continueront à rechercher des produits dans le catalogue, à passer par l'agrégateur, à acheter s'ils sont déjà autorisés, ou à autoriser via OAuth2.
  • Ne négligez pas toutes sortes de services cloud.
  • Utilisez CDN non seulement pour optimiser les délais du réseau, mais également comme moyen de protection contre les attaques d'épuisement des canaux et simplement d'inondation dans le trafic statique.
  • Il est nécessaire de recourir à des services de protection spécialisés. Vous ne pouvez pas vous protéger des attaques L3&4 au niveau du canal, car vous ne disposez probablement tout simplement pas d'un canal suffisant. Il est également peu probable que vous combattiez les attaques L7, car elles peuvent être très importantes. De plus, la recherche de petites attaques reste l'apanage de services spéciaux, d'algorithmes spéciaux.
  • Mettez à jour régulièrement. Cela s'applique non seulement au noyau, mais également au démon SSH, surtout si vous les avez ouverts vers l'extérieur. En principe, tout doit être mis à jour, car il est peu probable que vous puissiez détecter vous-même certaines vulnérabilités.

Source: habr.com

Ajouter un commentaire