FRnOG 37 - Amaury Denoyelle & Willy Tarreau : HTTP/3 - QUIC, quels impacts sur la sécurité ?

  • l’année dernière
Amaury Denoyelle & Willy Tarreau : HTTP/3 - QUIC, quels impacts sur la sécurité ? #FRnOG
Transcript
00:00 Bonjour, donc nous on développe sur HAProxy et HAProxy a une stack QUIC HTTP 3 depuis à peu près un an maintenant qui commence à être utilisé
00:14 et on a pas mal de gens qui commencent à nous poser des questions, qui se posent des questions sur les impacts que ça a en termes de sécurité de déployer du QUIC
00:22 donc c'est ce qu'on va aborder ici.
00:24 Moi je vais essentiellement relayer les questions et puis je vais laisser Amaury répondre puisque c'est lui qui sait en fait, il aime pas que je dise ça.
00:33 Bon bah voilà, qu'est-ce que c'est que le QUIC ?
00:36 Donc déjà un bref rappel sur HTTP 3, comme son nom l'indique c'est la troisième génération du protocole HTTP
00:43 et il repose sur le nouveau protocole de transport QUIC.
00:47 Donc pourquoi un nouveau protocole de transport ? Pour ça il faut remonter au HTTP 2 pour comprendre.
00:52 Donc le H2 a apporté pas mal de nouveaux progrès intéressants, notamment le plus important c'était la notion de stream
01:00 qui permettait à un client de pouvoir lancer plusieurs requêtes en parallèle vers un serveur sur les mêmes connexions.
01:06 Donc ça c'était un beau progrès mais par contre comme H2 repose toujours sur TCP en fait on a quand même une interdépendance entre les streams.
01:16 A savoir que s'il y a une perte de paquet, donc une perte d'un seul segment TCP, la connexion TCP, donc la réception va être bloquée
01:24 en attendant qu'on reçoive le segment manquant et donc du coup non seulement le stream perdu va être impacté
01:32 mais également les streams qui peuvent suivre.
01:35 Donc pour résoudre ce problème en fait il a été décidé de créer un nouveau protocole de transport, donc le QUIC.
01:42 QUIC, c'est un nouveau protocole de transport implémenté par-dessus UDP en fait.
01:49 Donc ça va permettre de l'implémenter en espace utilisateur.
01:53 Son rôle c'est de faire à peu près la même chose que TCP, donc il fournit une notion de connexion, il gère la retransmission de données
02:03 et il permet de transmettre les données dans l'ordre.
02:06 Sauf que cette fois les données sont ordonnées par stream puisque QUIC a la notion de stream maintenant.
02:12 Et donc du coup si on perd un paquet pour un stream on va pas interrompre la transmission pour les autres streams.
02:18 QUIC, donc en plus de la notion de stream il y a quelques features en plus qui ont été ajoutées,
02:25 notamment le fait que c'est un protocole qui est obligatoirement sécurisé, donc chiffré avec du TLS 1.3.
02:33 Il fournit aussi des fonctionnalités comme par exemple les migrations de connexion,
02:38 donc un client peut changer d'adresse IP et maintenir sa connexion.
02:41 Et donc du coup par dessus on retrouve à nouveau HTTP/3, et HTTP/3 en fait on peut dire que c'est comme du HTTP/2
02:48 mais sans la notion de stream puisque celle-ci a été descendue au niveau du dessous.
02:52 Donc là on a un petit schéma qui montre une comparaison entre les deux stacks.
02:58 Donc à gauche une pile HTTP/2 sur TCP.
03:04 Donc ce qu'on voit d'intéressant c'est que le carré QUIC est beaucoup plus gros que les autres
03:10 puisqu'il reprend beaucoup de fonctionnalités qui étaient jusqu'à présent éparpillées entre les différents protocoles.
03:17 Et donc tout ce protocole là va tourner en espace utilisateur,
03:23 donc on aurait pu croire que ça pouvait générer une console CPU un peu supérieure,
03:27 ce qui était le cas au début des expérimentations sur QUIC, mais ce problème a été résolu depuis.
03:34 Et là on a un zoom sur notre pile QUIC avec HTTP/3 au-dessus.
03:41 Et donc là on voit les différentes étapes de parsing d'un paquet.
03:45 Donc on voit tout en bas qu'on reçoit des datagrammes, ensuite on split ça en paquets QUIC qui sont chiffrés,
03:51 on les déchiffre et on les démuxe pour les différents streams.
03:56 Et donc ce qui va être intéressant pour la sécurité c'est de pouvoir arrêter les paquets malveillants
04:00 le plus tôt possible dans la chaîne pour consommer le moins possible de ces puits de RAM.
04:06 Parmi les réticences qu'on entend souvent quand on propose aux gens de commencer à faire des tests sur QUIC,
04:14 on entend dire "mais c'est de l'UDP, moi j'aime pas l'UDP, j'en ai viré plein dans mon datacenter,
04:19 j'arrive bientôt à ne plus en avoir, c'est pas pour revenir en arrière".
04:21 Parce que l'UDP, vous le savez tous, on en a parlé juste avant,
04:24 donc c'est le spoofing, les attaques d'amplification, tout ce qui est floude.
04:28 C'est les fuites de données aussi parce que le protocole n'est pas orienté.
04:31 Donc si on fait du filtrage à base d'ACL stateless, on peut aussi faire ressortir des paquets du réseau interne
04:37 et piquer des données par là.
04:38 Un autre problème que présente QUIC aussi c'est qu'il fonctionne sur des ports complètement arbitraires.
04:45 Donc là où HTTP habituellement les browsers se connectent sur le port 80 ou le 443,
04:51 QUIC en fait aujourd'hui on s'y connecte après avoir déjà appris que le service était disponible quelque part
04:56 et on peut vraiment choisir n'importe quel port.
04:58 Les usages ont tendance à converger vers l'usage du port 443 mais quand même,
05:03 ça fait partie des choses qui inquiètent un petit peu les utilisateurs
05:06 parce qu'on n'a pas envie de laisser le réseau ouvert à tous les vents.
05:08 Il y a aussi le fait que le protocole soit très jeune,
05:12 qu'il n'est pas encore pris en compte par les outils, les produits de sécurité.
05:16 Je pense notamment les firewalls, aujourd'hui ils vont voir un bête flux UDP,
05:19 ils vont pas aller regarder plus loin.
05:21 Des outils d'anti-DDoS, il y en a peut-être qui commencent à regarder QUIC
05:24 mais je sais pas jusqu'où ils vont.
05:26 Ça reste assez limité et ça pose encore des soucis.
05:29 Le logging, n'en parlons pas, à part logger qu'on a vu des paquets qui font partie d'une même session,
05:34 ça va pas beaucoup plus loin sur les équipements Stateful.
05:37 Et puis ce qu'on disait juste avant sur la console CPU,
05:40 effectivement il y a eu beaucoup de problèmes au début où on parlait d'une console x4 à peu près.
05:45 Aujourd'hui on relève plus de l'ordre de quelques pourcents à quelques dizaines de pourcents dans les pires cas.
05:51 Mais pour vous donner un ordre d'idées, sur des tests qu'on a fait cette semaine,
05:55 on sortait 200Go sur une machine à 56 heures.
05:58 Donc c'est quand même tout à fait honorable.
06:01 On va parler de différents types d'attaques, différentes catégories d'attaques,
06:06 notamment comment est-ce que vous allez vous protéger contre des attaques visant des tiers.
06:14 Typiquement des attaques d'amplification notamment.
06:18 L'idée c'est qu'un attaquant va spouffer justement une victime
06:23 et essayer de vous envoyer des requêtes en espérant que le trafic sera beaucoup plus volumineux
06:29 et qu'il va faire du mal à la victime.
06:31 C'est ce qui a été dit juste avant par Richard sur les attaques de réflexion,
06:35 typiquement NTP ou DNS classiquement.
06:38 Pour ça le protocole QUIC a pas mal de protections, de parades.
06:42 Déjà il oblige les paquets initiaux à être au moins de 1,2kg.
06:47 Les paquets initiaux ce sont ceux pour ouvrir des nouvelles connexions.
06:51 Donc déjà ça va nécessiter à l'attaquant de générer des paquets assez gros pour son attaque.
06:57 Et en plus il y a un mécanisme d'anti-amplification côté serveur.
07:02 Le serveur ne pourra pas émettre trois fois plus que la quantité de données qu'il a déjà reçue du client
07:08 tant que la roundshake n'est pas établie.
07:10 Donc ça limite encore plus la portée de l'attaque.
07:13 Et puis il y a un mécanisme très pratique, la génération de paquets retry.
07:19 Donc ça c'est un peu l'équivalent du TCP SYNCOOKIE.
07:23 C'est qu'en fait le serveur, plutôt que directement balancer la sauce avec les paquets cryptos pour ouvrir la connexion,
07:29 il va émettre un petit paquet retry avec un token
07:33 et le client est censé réémettre son paquet initial avec ce token.
07:38 Donc là avec ça déjà on se protège pas mal contre l'attaque.
07:43 Et puis pour tous les autres types de paquets qui ne sont pas des paquets initiaux,
07:48 là le serveur va juste pas les répondre parce qu'il connaîtra pas la connexion QUIC.
07:52 Après il y a un autre type d'attaque qu'on voit de temps en temps sur des protocoles basés sur UDP.
07:58 C'est tout ce qui permet d'essayer de joindre des machines qui sont normalement inaccessibles de l'extérieur.
08:03 Donc des attaques de relayage de trafic.
08:05 Donc typiquement vous avez un serveur qui est ouvert dans une DMZ avec un port echo ouvert
08:10 et vous balancez un paquet en spouffant une IP interne.
08:12 Il n'y a pas de filtrage et puis le serveur va répondre en renvoyant le paquet au serveur interne.
08:17 Si vous avez une backdoor qui attend du shellcode ou n'importe quoi là-dessus,
08:22 potentiellement vous pouvez faire exécuter des ordres en interne.
08:25 Donc ça on le voit avec de l'echo, on le voyait avant avec le RPCSS,
08:29 on pouvait faire du ping-pong, on avait des protocoles comme ça.
08:31 Donc pour QUIC, du coup, qu'est-ce qu'il en est ?
08:34 Donc du coup si on veut utiliser un serveur QUIC pour ce genre d'attaque,
08:37 ça va être quand même assez compliqué à mettre en place
08:40 puisque le formatage des paquets est assez rétractif.
08:44 Donc déjà il y a un premier octet qui va comporter plusieurs infos,
08:48 notamment le type de paquet, qui va être à peu près fixe.
08:53 Et puis ensuite il va y avoir la version QUIC également sur 4 octets.
08:59 Et enfin le CID, donc c'est le Connection ID qui lui est choisi de manière aléatoire par le serveur,
09:05 sur lequel l'attaquant a aucunement la main.
09:08 Et donc du coup ça fait déjà en tête de paquet 25 octets que l'attaquant ne connaîtra pas.
09:14 Donc pour réaliser ce type d'attaque, il va falloir s'accrocher.
09:18 Après on a d'autres types d'attaques, c'est celles qui visent à faire tomber l'infrastructure
09:23 ou au moins lui faire prendre assez cher comme on dit.
09:26 Donc le truc classique de consommer plein de CPU par du flux de paquets,
09:32 là l'idée c'est que comme la stack QUIC est en userland,
09:36 c'est sûr que faire passer un paquet UDP à travers le kernel pour arriver sur une application userland,
09:41 on se dit que ça coûte plus cher.
09:44 Donc en gros là l'idée c'est de flouder énormément de paquets,
09:48 soit des petits, soit des gros, mais généralement plutôt des petits,
09:52 pour essayer de maximiser le coût de traversée et de faire mal à la couche QUIC qui est derrière.
09:58 Donc pour ça, si l'attaquant génère des petits paquets,
10:03 là ils seront assez vite rejetés par le serveur en contrôlant assez vite le CID,
10:08 puisque le CID est en clair.
10:12 Ensuite si l'attaquant essaie quand même d'utiliser des paquets initials pour passer ce filtre,
10:19 il faut donc que je l'ai dit qu'il génère des paquets déjà de au moins 1,2 KB,
10:24 donc ça limite quand même fortement la portée de l'attaque.
10:27 Et si le serveur est bien configuré, il va assez vite comprendre qu'il se passe quelque chose d'anormal,
10:32 et il va commencer à envoyer du retry, et le retry c'est stateless,
10:38 donc ça ne va pas consommer trop de ressources pour le serveur.
10:43 Et puis on a aussi la possibilité, au vu de comment sont formés les paquets,
10:49 d'implémenter du filtrage stateful à assez bas niveau,
10:54 notamment avec XDP au niveau du kernel.
10:57 On peut carrément faire la génération des paquets retry à ce niveau là,
11:03 et donc ne pas taper la partie user space.
11:07 Donc une autre approche ça peut être d'essayer de faire consommer énormément de RAM
11:13 en floodant avec des paquets choisis.
11:16 C'est ce qui se fait par exemple sur des attaques de fragmentation, de segmentation ou autre,
11:21 où on envoie de la donnée avec un trou et puis de la donnée derrière,
11:24 en espérant que le serveur va mémoriser énormément de choses,
11:27 et consommer plein de ressources en attendant les données qui ne viennent pas.
11:31 Donc voilà, ça c'est relativement classique.
11:34 Dans le cadre de QUIC, ça donnerait quoi ?
11:36 Dans le cadre de QUIC, on a le flow control qui est là pour protéger contre ce genre de problèmes.
11:42 Donc le flow control, on a différents paramètres qu'on peut fixer.
11:45 On peut fixer notamment, pour chaque stream, la quantité de données qu'on est prêt à recevoir,
11:52 de manière indépendante pour chaque stream.
11:54 Et on peut aussi fixer la limite du nombre de streams que le client peut ouvrir en simultané.
11:59 Et donc comme tous ces paramètres en plus, comme QUIC est implémenté en user space,
12:04 on peut changer ces paramètres assez facilement au niveau de l'applicatif serveur.
12:09 Et puis comme je l'ai dit au début, comme les streams sont indépendants au niveau de QUIC,
12:16 même s'il y a des streams qui ne progressent pas, ça ne va pas gêner les streams de la connexion.
12:23 Alors, une autre approche pour essayer de faire tomber notre serveur,
12:28 ça va être de se pouffer massivement des connexions.
12:31 Donc typiquement ce qu'on fait sur du TCP habituellement,
12:36 ne serait-ce qu'avec des SIN, en laissant plein de connexions en SIN/RECEIVE en face.
12:41 Dans le cadre de QUIC du coup.
12:44 Donc là encore c'est le mécanisme de retry qui va rentrer en jeu.
12:49 Notamment, normalement le serveur va assez vite voir qu'il y a beaucoup de connexions qui sont en cours d'ouverture,
12:56 mais qu'il ne se passe rien, donc il va émettre des retry avec des tokens.
13:01 Et l'attaquant a besoin d'être sur le chemin pour récupérer ce token,
13:06 donc ça va être assez compliqué d'aller plus loin pour lui.
13:12 Alors, une autre approche ça peut être d'essayer de monter énormément de connexions sur le serveur.
13:18 Donc là en général ça va plus se faire depuis des machines qui ont déjà été piratées,
13:23 donc des machines sacrifiables.
13:26 Essayer de monter plein de connexions dessus et de les maintenir le plus longtemps possible.
13:31 Donc là si l'attaquant a réussi à maintenir la connexion, il y aura quand même le problème de l'idée timeout.
13:37 À savoir, QUICK impose un timeout.
13:42 S'il n'y a pas eu d'échange de paquets pendant ce délai, la connexion peut être fermée de manière directe.
13:49 Et donc ce timeout est négocié entre le client et le serveur, on prend la valeur la plus basse,
13:53 donc on a totalement la main dessus.
13:56 Et puis si l'attaquant essaie quand même de maintenir la connexion en envoyant des trames bidons,
14:01 par exemple des trames de type ping, qui sont des trames QUICK, pour bypasser ce timeout,
14:07 là dans ce cas il faudra avoir une solution un peu plus haut niveau.
14:11 Donc par exemple côté HAProxy, on a le mécanisme de timeout sur les streams,
14:15 on va voir qu'il n'y a aucune donnée réelle qui soit échangée et on va finir par fermer la connexion.
14:20 Et on a aussi implémenté une limite sur le nombre de retransmissions qu'on permet pour chaque trame.
14:27 Donc si on détecte qu'une trame a été retransmise à partir d'un certain nombre de fois,
14:32 et qu'il n'y a toujours pas d'acquittement reçu, on considère la connexion comme malade et on la ferme directement.
14:38 Alors on peut envisager aussi de monter plein de streams concurrents,
14:43 donc ça ce sont des attaques qui sont faciles à faire déjà sur du HTTP/2.
14:47 Dans une seule connexion, vous avez vite fait de balancer 100 requêtes en quelques dizaines, voire centaines d'octets.
14:53 Et à ce moment là, derrière c'est pareil, ça va réserver des ressources, peut-être des buffers ou des choses comme ça.
14:59 Et de la même manière, on peut envisager aussi de formuler des requêtes vers le serveur et de ne pas lire les réponses.
15:04 C'est un mécanisme qui est connu sous le nom de slow read en général.
15:07 Donc soit on ne les lit pas du tout, soit on les lit octet par octet pour bloquer les données le plus longtemps possible dans des buffers.
15:12 Donc ça, ça va être pris en charge par le flow control en partie.
15:16 Je l'ai dit que le flow control permet de fixer une limite sur le nombre de streams qu'on accepte d'ouvrir.
15:23 Comme en H2, cette limite, on peut la choisir librement côté serveur.
15:29 Et puis pour les attaques de type slow read, là la SPEC ne propose pas vraiment de parade.
15:36 En tout cas, nous côté HAProxy, on a les timeouts qui sont là, les timeouts HAProxy, donc timeouts clients.
15:43 Et puis on a aussi une limite de buffers, on alloue pas des buffers infinis pour chaque connexion.
15:51 Alors après, on a d'autres cas de figure, c'est s'il y a des abus répétés.
15:57 Donc un client qui est connecté, par exemple un bout de javascript qui tourne dans un browser,
16:02 qui balance des milliers de requêtes en boucle ou des choses comme ça.
16:05 Donc habituellement, ce qu'on fait sur HAProxy, c'est qu'on a des mécanismes à base de stick table,
16:11 on a des track counters, on a des mécanismes comme ça où on va pouvoir compter le nombre de requêtes par connexion,
16:15 le nombre de requêtes sans cookie, enfin tout un tas de combinatoires comme ça.
16:19 Et puis décider de bloquer quand des seuils sont franchis ou que le trafic est caractérisé comme étant anormal.
16:26 Ça s'applique sur des flux TCP traditionnellement.
16:29 Donc là, la question qui se pose, c'est qu'est-ce qu'il en est de QUIC, vu que QUIC c'est basé sur du DP ?
16:34 En tout cas, côté HAProxy, au vu de notre implémentation, on a implémenté QUIC de manière un peu similaire,
16:41 enfin au même niveau que TCP.
16:43 Et en fait, on peut appliquer de manière identique les mêmes règles, donc les règles TCP/EQUEST vont aussi s'appliquer sur le trafic QUIC.
16:52 Alors, l'un des derniers points qui vont rester, c'est d'une manière générale tout ce qui concerne le filtrage des flux.
17:00 On le disait tout à l'heure, le fait que très souvent le filtrage UDP soit purement stateless sur des équipements Edge,
17:07 ça a tendance à laisser fuir facilement de l'info.
17:11 Donc, le fait qu'on laisse passer QUIC, on peut se dire qu'on va être obligé de mettre des règles pour laisser passer ce trafic.
17:19 Qu'est-ce que ça va impliquer ? Est-ce que ça veut dire qu'on peut nous mettre des backdoors en UDP sur des serveurs où il y a du QUIC qui tourne ?
17:26 Ou est-ce qu'il faut laisser des ports ouverts ? Qu'est-ce que ça implique du coup de faire ça ?
17:32 Là, il n'y a pas vraiment de solution miracle, c'est plutôt des bonnes pratiques.
17:37 C'est d'essayer de restreindre au maximum les plages ZPIC sur lesquelles on ouvre le trafic, également les plages de ports.
17:46 On a dit QUIC, il n'y avait pas de port spécifié par la SPEC, mais en pratique, on voit bien que c'est quand même le port 443 qui reste majoritairement utilisé.
17:57 A priori, à l'heure actuelle, les browsers n'ont pas l'air d'autoriser des connexions QUIC sur d'autres ports.
18:03 Donc là-dessus, on peut quand même restreindre assez fortement.
18:07 Puis on a vu aussi qu'on pouvait faire du filtrage stateful pour se prévenir en plus là-dessus.
18:12 Après, on va parler des attaques sur les données. Donc là, ça va concerner plus la partie HTTP en elle-même.
18:19 D'ailleurs, pas tout de suite le HTTP. Il y a un bout sur QUIC. On a dit que le protocole est sécurisé.
18:24 Donc on se pose les questions sur est-ce qu'on peut intercepter de la donnée ? Est-ce qu'on peut la rejouer ?
18:30 Est-ce qu'on peut essayer de s'insérer dans une connexion ? Et surtout, qu'est-ce qu'il en est de la capacité à traquer les utilisateurs en observant des connexions ?
18:37 Donc là-dessus, QUIC, on a dit, c'était chiffrement obligatoire avec TLS 1.3. Donc on a les dernières protections à la mode.
18:45 On a aussi une partie de l'entête qui est chiffrée. Pas toute l'entête, mais une partie quand même.
18:51 Et puis, pour le tracking des connexions, en fait, je l'ai dit, le connexion ID est en clair.
18:58 Sauf qu'on peut en allouer plusieurs par connexion et donc le client va pouvoir switcher de connexion ID pour essayer de se dissimuler.
19:05 Ok, après là, on va passer à la partie sur les applications. Donc là, ça va être notamment le H3.
19:13 Donc la question qui se pose, c'est de se dire, mais est-ce que le protocole HTTP/3 en lui-même, il ne va pas fragiliser à nouveau les applications,
19:20 comme on l'a vu avec HTTP/2 où on pouvait faire passer des headers binaires, des caractères interdits comme des soldes links et des choses comme ça dans les headers.
19:28 Qu'est-ce qu'il en est du coup pour H3 ?
19:30 Donc le H3, c'est à peu près comme le H2, on l'a vu en présentation. Et donc du coup, c'est à peu près les mêmes règles.
19:36 Donc pour les attaques de désynchronisation, on est protégé par le fait qu'il ne peut y avoir qu'une seule requête/réponse par stream.
19:42 Ensuite, pour les attaques de type smuggling, donc en général, c'est en essayant de balancer des requêtes avec des headers content-length pourris.
19:51 Là-dessus, on réutilise les mêmes règles de sécurité que les protocoles HTTP déjà définis.
19:58 Donc il faut privilégier la réutilisation du code existant et on est protégé contre tout ça.
20:05 Dernier point, avec QUIC, donc QUIC a été développé majoritairement pour HTTP/3, mais on peut maintenant supporter d'autres protocoles.
20:13 Donc il y a plusieurs expérimentations qui sont menées là-dessus.
20:16 Et donc, par exemple, côté H4/XI, nous on supporte aussi le HTTP/09, donc juste à des fins de diagnostic.
20:23 Et ça est utilisé aussi pas mal pour l'interopérabilité entre les différents stacks.
20:28 Donc bon, il faut juste vérifier à ne pas l'activer en production, ce serait dommage.
20:32 Si vous avez plus de questions sur la sécurité dans QUIC, je vous invite à regarder la RFC 9000, il y a une section dédiée là-dessus.
20:40 Voilà, c'est tout.
20:43 (Applaudissements)

Recommandée