Il s'agit du quatrième blog d'une série de blogs qui couvrent divers aspects de ce qu'il nous a fallu pour créer et exploiter notre service SaaS :
Dans le blog précédent , nous avons fourni des informations sur le défi que représente l’utilisation de techniques cryptographiques pour sécuriser notre plateforme (infrastructure, applications et données). Ce blog traitera des techniques que nous utilisons pour sécuriser la plateforme contre les attaques ciblées provenant du réseau — depuis Internet ainsi que de l’intérieur. Étant donné que les applications ne sont plus limitées à un emplacement physique, les pare-feu traditionnels basés sur le périmètre et les solutions de sécurité basées sur les signatures ne sont plus efficaces. Nous décrirons les lacunes de notre implémentation initiale de sécurité zero-trust et pourquoi + comment nous l'avons augmentée avec l'apprentissage automatique et les techniques algorithmiques pour sécuriser correctement notre infrastructure distribuée + nos clusters d'applications.
Notre plateforme exécute un grand nombre d'applications au sein de plusieurs équipes qui exploitent leurs propres clusters en périphérie, notre réseau mondial et les clouds publics AWS et Azure. Alors que la majorité des charges de travail sont des microservices orchestrés à l'aide de Kubernetes, nous disposons d'une poignée de monolithes à très grande échelle (par exemple, elasticsearch) que nous gérons à l'aide de Terraform. La figure 1 démontre la nature distribuée de notre plateforme. Par exemple, dans chacun de nos plus de 18 points de présence (PoP) de réseau mondial (avec quelques dizaines à un peu plus d'une centaine de serveurs physiques), nous exécutons des milliers de pods d'applications. Cependant, à la périphérie, nous avons aujourd’hui des déploiements clients individuels avec plus de 3 000 emplacements actifs (chacun avec un à sept calculs) exécutant quelques dizaines de pods d’applications.
La plateforme est entièrement multi-locataire, chaque nœud exécutant des charges de travail provenant de différents clients (et les nôtres). Étant donné que certaines de ces applications sont exposées à l’Internet public, nous devons nous assurer que toutes les communications vers/depuis les applications sont sécurisées. Comme nous l'avions souligné dans les deux blogs précédents, nous avons construit un système robuste d'identité, d'authentification et d'autorisation ainsi que notre propre chemin de données réseau L3-L7+ (VoltMesh) qui est utilisé pour alimenter notre maillage de services et notre passerelle API . Comme le montre la figure 2, cela nous a permis de fournir une sécurité au niveau du transport sur les clusters d'applications (mTLS), des utilisateurs (TLS/mTLS) et des employés (mTLS), ainsi qu'un contrôle d'accès basé sur l'authentification + l'autorisation.
Bien que cette implémentation de confiance zéro offre de nombreux avantages, elle ne résout pas automatiquement plusieurs problèmes de sécurité :
Au cours des 2,5 dernières années de développement sur cette plateforme, nous avons également réalisé que souvent nos développeurs intègrent des applications open source, les conteneurisent et demandent à notre équipe DevOps de les déployer sur la plateforme. Cependant, ils manquent souvent de détails sur les interactions au niveau de l'API au sein de ces applications, dont notre équipe de sécurité a besoin pour créer des politiques visant à mettre sur liste blanche la communication. Il s’agit d’un obstacle majeur pour notre implémentation de sécurité Zero Trust, car elle impose des politiques de liste blanche qui autorisent uniquement les API utilisées par les applications et bloquent tout autre trafic. Chaque fois que nous avons fait des exceptions à cette exigence, certaines applications se sont retrouvées avec une segmentation très basique au niveau du réseau, augmentant ainsi la surface d’attaque.
Par conséquent, nous avons dû compléter notre solution de sécurité Zero Trust existante avec des fonctionnalités de sécurité supplémentaires pour gérer les problèmes énumérés ci-dessus. Nous avons identifié une liste de fonctionnalités de sécurité supplémentaires que nous devions intégrer à la plateforme :
Nous avons décidé d’utiliser une combinaison de techniques traditionnelles basées sur la signature, d’algorithmes statistiques et d’approches d’apprentissage automatique plus dynamiques pour résoudre ces problèmes. Cela nous a obligé à apporter des modifications à notre backend SaaS ainsi qu'à ajouter de nouvelles fonctionnalités dans notre chemin de données réseau.
Afin de sécuriser la plateforme, nous autorisons uniquement les connexions réseau basées sur la liste blanche des API pour chaque application. Cela nécessite que notre équipe de sécurité se coordonne avec les développeurs et s'assure que notre moteur de politique programmable est alimenté avec les bonnes informations API. Nous avons rapidement réalisé qu’il était impossible pour nos développeurs de fournir ces informations pour les applications qui n’étaient pas créées à l’aide de notre framework de services.
Étant donné que notre proxy de service mesh se trouve dans le chemin réseau de chaque accès à l’application, nous avons décidé d’apprendre les API et les ressources statiques exposées par l’application en effectuant une analyse d’exécution de chaque accès qui passe par le proxy. Le défi avec cette approche est d’identifier les points de terminaison de l’API en inspectant les URL et en séparant les composants générés dynamiquement. Par exemple, pour une API « api/user/<user_id>/vehicle/ », le proxy verra des accès comme :
Il peut y avoir des millions de telles demandes, ce qui les rend très difficiles à déchiffrer. Par conséquent, l’identification des composants dynamiques dans ces requêtes associées est effectuée à l’aide de l’apprentissage profond et de l’analyse graphique. Nous représentons l'ensemble des composants URL sous forme de graphique, puis effectuons un clustering de graphiques pour trouver des sous-graphiques avec des propriétés similaires à l'aide d'ensembles de fonctionnalités qui capturent des propriétés spécifiques de composants générés dynamiquement tels que :
En conséquence, les composants dynamiques sont classés et la sortie du système ressemble à ceci :
En utilisant cet apprentissage automatique des API, nous pouvons facilement et automatiquement générer une politique qui peut être appliquée par notre proxy de service mesh. Sur la base des points de terminaison d'API découverts, nous apprenons également d'autres propriétés telles que les applications qui utilisent quelles API pour communiquer avec d'autres applications, le comportement typique de ces API, etc. Cela nous permet de créer un graphique de service qui aide notre équipe de sécurité à visualiser l'interaction de service à service pour l'analyse médico-légale, la découverte et la micro-segmentation au niveau de l'API.
Avant de nous lancer dans l’ajout des deux fonctionnalités restantes (détection d’anomalies et profilage de comportement), nous avons décidé de voir si les solutions existantes pouvaient nous aider. Bien qu'il existe de nombreux pare-feu périmétriques et produits de pare-feu pour applications Web sur le marché, la majorité de ces solutions sont conçues pour protéger les applications connectées à Internet. Ils partent du principe que le trafic traité est du trafic Web et fournissent une protection ciblée pour HTML, JavaScript, SQL, CMS, etc., ce qui facilite relativement l'écriture de signatures et de règles pour détecter les vulnérabilités et les exploits connus.
Bien que cette capacité soit importante pour notre trafic Web, nous devons également gérer une quantité croissante de trafic API et machine à machine dans notre environnement. Pour résoudre ce problème, notre équipe de sécurité devrait rédiger des règles spécifiques aux applications qui ne relèvent pas des règles Web typiques connues (comme OWASP CRS). En général, les administrateurs de sécurité connaissent peu les applications et, compte tenu de la nature dynamique de l’environnement, il devient encore plus difficile de suivre les types d’applications et la structure pour écrire ces règles spécifiques aux applications. Par conséquent, même si notre équipe de plateforme fournit cette capacité dans notre chemin de données réseau, elle n’est pas souvent utilisée par notre équipe de sécurité.
Un autre problème pour lequel nous disposons d’une quantité importante de données provenant de notre réseau est que les attaques d’applications deviennent de plus en plus sophistiquées au fil du temps. L'attaquant passe des jours à effectuer une reconnaissance pour déterminer les détails des API, de l'application, de l'infrastructure sous-jacente et du type de système d'exploitation en examinant les signatures HTTP/TCP, etc. Les approches traditionnelles basées sur les signatures et les règles sont d’une utilité très limitée dans ces situations et nous avons décidé de poursuivre notre approche basée sur l’IA pour apprendre automatiquement le comportement des utilisateurs et appliquer les bons et les mauvais comportements.
La plupart des applications ont certains flux de travail (séquence d'API) et contexte (données au sein des API) pour lesquels différents cas d'utilisation/déploiements sont conçus et généralement suivis par les utilisateurs des applications. Nous exploitons ces propriétés et formons nos algorithmes d’apprentissage automatique pour modéliser des modèles comportementaux « valides » dans une interaction utilisateur typique avec l’application.
Notre chemin de données échantillonne les requêtes/réponses pour chaque API ainsi que les données associées et les envoie à notre moteur d'apprentissage central, comme indiqué dans la figure 3. Ce moteur génère et met à jour en permanence le modèle de modèles comportementaux valides qui est ensuite utilisé par le moteur d'inférence exécuté dans le chemin de données pour alerter/bloquer les comportements suspects.
Le moteur d'apprentissage examine de nombreuses mesures telles que la séquence des API, les écarts entre les requêtes, les requêtes répétées aux mêmes API, les échecs d'authentification, etc. Ces mesures sont analysées pour chaque utilisateur et sur une base globale pour classer les bons et les mauvais comportements. Nous effectuons également un regroupement de comportements pour identifier plusieurs séquences différentes de « bon comportement ». Prenons un exemple pour illustrer cela :
La séquence d'API suivante sera signalée par le système comme un comportement suspect/mauvais qui sera automatiquement atténué par le système ou générera une alerte pour qu'un administrateur intervienne
Depuis que nous avons mis ce système en production il y a plus d’un an, nous avons continuellement affiné le modèle en fonction de l’utilisation et des commentaires des clients. Nous avons pu identifier avec succès les types d'attaques suivants :
Cela dit, nous avons également réalisé que cette approche présente certains problèmes : elle ne peut pas détecter les attaques lentes et de faible intensité (force brute, déni de service d’application, scanner) pour lesquelles nous devons appliquer des techniques de détection d’anomalies.
Parfois, nous voyons des attaques très sophistiquées qui utilisent de grands botnets distribués qui passent sous le radar de notre technique d’analyse comportementale. Voici quelques exemples de telles attaques :
Étant donné que notre chemin de données réseau collecte des informations à partir de chaque nœud de notre réseau mondial, il devient relativement facile d'effectuer une analyse sur les mesures globales d'une application particulière, telles que le taux de requête, le taux d'erreur, le débit de réponse, etc. Cette analyse nous permet de détecter les attaques distribuées et de les atténuer (à chaque nœud) en identifiant les utilisateurs qui pourraient faire partie de ces botnets. Prenons un exemple où nous essayons de détecter des anomalies sur différentes fenêtres temporelles (5 dernières minutes, 30 dernières minutes, 4 heures, 24 heures) en examinant les taux de demande et si le taux de demande est élevé dans une fenêtre temporelle donnée, alors l'analyse plus approfondie suivante des journaux d'accès sera effectuée par le système :
Bien que la détection des anomalies ait toujours été une technique importante pour la détection et la prévention des intrusions (IDS/IPS) dans les dispositifs de pare-feu, ces dispositifs ne sont pas en mesure d’atténuer les attaques globales au niveau de la couche applicative. Grâce à notre capacité à effectuer le balisage et l’apprentissage des API sur notre plateforme mondiale, nous sommes désormais en mesure de supprimer les attaques à la source sur l’ensemble de notre réseau distribué.
Bien que nous ayons été extrêmement satisfaits de notre implémentation Zero Trust basée sur un service mesh et une passerelle API, nous avons réalisé qu'elle n'était pas complète pour sécuriser les clusters d'applications distribuées contre les vulnérabilités et les attaques malveillantes. Nous avons dû l’enrichir avec l’apprentissage automatique pour l’analyse du comportement + la détection des anomalies ainsi que les techniques traditionnelles basées sur les signatures + les règles pour fournir une meilleure solution de sécurité.
Nous avons constaté trois gains significatifs grâce à l'ajout d'inférences distribuées dans notre chemin de données réseau L3-L7+ ainsi qu'à l'apprentissage du cœur exécuté dans notre SaaS centralisé :
La sécurité des réseaux et des applications est une piste sans fin et il semble que nous ayons encore un long arriéré de nouvelles fonctionnalités à ajouter. Nous reviendrons dans un avenir proche pour partager des informations supplémentaires sur les algorithmes et techniques incrémentaux que nous avons mis en œuvre.
Cette série de blogs couvrira divers aspects de ce qu'il nous a fallu pour créer et exploiter notre service SaaS distribué à l'échelle mondiale avec de nombreux clusters d'applications dans des clouds publics, nos PoP de réseau privé et nos sites périphériques. La prochaine étape sera « L’observabilité sur notre plateforme distribuée à l’échelle mondiale » (à venir)…