L’une des choses intéressantes – et les plus frustrantes – que j’ai constatées au fil des années est la différence entre la façon dont les ingénieurs réseau et les développeurs d’applications voient les applications. Nous l'avons vu dans la manière dont les applications sont représentées sur les diagrammes de réseau et, inversement, dans la manière dont les réseaux sont représentés sur les diagrammes d'architecture d'application.
Il va sans dire que chacun a une vision très simpliste de l’autre.
C’est également vrai pour la sécurité, dont le rôle dans la protection non seulement des réseaux et des applications mais aussi de l’entreprise elle-même est devenu primordial. Il est important que les équipes de sécurité sortent des sentiers battus et utilisent des diagrammes d'architecture classiques pour vraiment comprendre les applications. Un pourcentage important d’attaques (réussies) sont exécutées au niveau de la couche applicative. Plus nous ne parvenons pas à reconnaître les caractéristiques uniques des différents types d’applications, plus ces applications restent vulnérables.
La discussion d’aujourd’hui portera sur les SPA. C'est ce qu'on appelle des « applications à page unique » pour ceux qui se demandent ce que signifie ce TLA particulier.
Une application monopage est simplement cela : une seule page Web qui sert de cadre pour toutes les tâches liées à l'application. Cette architecture rappelle l'époque du Web 2.0 et l'émergence d'AJAX comme méthode de « rafraîchissement » des éléments DOM (Document Object Model) individuels plutôt que de recharger la page entière. Les performances ont été grandement améliorées grâce à cette technique, qui constitue le précurseur de l'application moderne à page unique.
Ces applications reflètent plus fidèlement le comportement d'une application mobile, où l'interface utilisateur du client est chargée lors de sa première ouverture et la communication avec le serveur implique uniquement des données. Cela signifie que chaque appel au serveur ne contient rien de plus que des données et que toutes les modifications apportées à l'interface utilisateur se produisent sur le client. Cela réduit considérablement la quantité de données envoyées dans les deux sens et, comme vous pouvez l’imaginer, signifie de meilleures performances. Ces types d’applications utilisent des API pour échanger des données.
En général, nous partons du principe que la plupart de ces API sont implémentées à l’aide des principes REST. Cela signifie que chaque objet (généralement considéré comme lié à un seul élément d'interface utilisateur) possède sa propre API qui peut être invoquée pour exécuter des transactions CRUD (créer, lire, mettre à jour et supprimer). D'un point de vue de la sécurité, cela rend les choses un peu plus faciles car vous pouvez vous attendre à des données dans des formats spécifiques pour un appel d'API (URI) donné. Les données envoyées à « /update/product/123 » seront systématiquement le même objet sérialisé, tandis que les données envoyées à « /delete/order/4433 » seront un objet sérialisé différent. Cela signifie que les pratiques traditionnelles consistant à lier des politiques spécifiques à des URI spécifiques peuvent être utilisées pour sécuriser cette API.
Désormais, les SPA peuvent – ou non – suivre ce modèle. Les pratiques émergentes autour des SPA peuvent utiliser et utilisent la même API pour effectuer des transactions ainsi qu'un mappage fonction-URI plus traditionnel. Avec un modèle d'URI unique SPA, l'URI ne change pas, mais les objets sérialisés envoyés dans les deux sens entre les serveurs le font.
Cela rappelle les transactions SOA/XML, dans lesquelles un seul point de terminaison (URI) serait utilisé pour appeler plusieurs fonctions. La fonction (point de terminaison) ciblée était contenue dans les données XML sérialisées ou parfois insérée dans un en-tête HTTP personnalisé. Ce modèle a conduit à la nécessité de passerelles SOA/XML chargées de recevoir la demande et de déterminer quelle fonction était invoquée avant de la router vers le bon point de terminaison (serveur).
Avec les SPA, vous pouvez être amené à traiter plusieurs appels d’API ou seulement quelques-uns. Dans tous les cas, vous êtes probablement confronté à la nécessité de sécuriser les données dans un format (peut-être XML, plus probablement JSON). Cela signifie que vous devez être conscient que le contenu comporte des risques. Étant donné que certaines données peuvent être utilisées pour générer dynamiquement des éléments d'interface utilisateur (ou manipuler le DOM sur le client), il est important de rechercher et de détruire le code potentiellement malveillant à chaque soumission.
Malheureusement, si un seul URI est utilisé pour échanger des données pour différentes fonctions, la pratique traditionnelle consistant à attacher des politiques aux URI ne fonctionnera pas. En fait, cela peut compromettre la sécurité, car ces politiques sont souvent formées pour s’attendre à des formats de charge utile spécifiques. Les passerelles API lient souvent elles aussi les politiques (routage, mesure, accès) à un URI spécifique. Cela signifie que la pratique consistant à utiliser seulement quelques URI (appels API) pour gérer beaucoup plus de fonctions avec des formats de données variés peut compromettre la sécurité existante.
Voici un exemple qui montre pourquoi il est de plus en plus important pour la sécurité et le développement (pas DevOps, mais les développeurs) de travailler plus étroitement dès la première ligne de code jusqu'au déploiement. Les développeurs peuvent prendre certaines mesures dès le début pour faciliter la mise en place de mesures de sécurité adaptées, comme l’insertion d’un en-tête HTTP avec un indicateur permettant d’exécuter la bonne politique. Quelque chose d'aussi simple que « X-Code : 'order' » enrichirait les requêtes de manière à ce que les solutions de sécurité puissent identifier - et ensuite analyser - les données à la recherche d'exploits potentiels.
D'un point de vue architectural, cela peut nécessiter un proxy L7 intelligent capable d'extraire le code et de réécrire l'URI avant d'acheminer la requête vers la solution de sécurité appropriée (WAF, API Gateway, etc.) Cette approche fonctionne également si le proxy L7 intelligent peut parler le langage du format de données, tel que JSON, et les développeurs incluent dans chaque échange un code ou un nom de point de terminaison qui peut être utilisé pour réécrire et acheminer vers le bon endroit.
Idéalement, l’intégration de la sécurité de la couche applicative dans l’application elle-même permettrait d’obtenir le meilleur équilibre entre performances et sécurité. Cela n’est cependant pas toujours possible et des solutions architecturales créatives sont parfois nécessaires pour atteindre l’objectif de sécurité.
D’une manière générale, les changements dans la manière dont les développeurs répartissent les différentes responsabilités entre le client et le serveur ont un impact significatif sur la manière dont l’infrastructure du service d’application interagit avec les demandes et les réponses ultérieures. Il est important que les équipes de sécurité, de développement et d'infrastructure des services d'application soient impliquées dans l'ensemble du cycle de vie du développement logiciel. La mise en œuvre d'une solution architecturale prend également du temps. Si vous attendez « jusqu'à la fin », vous gagnez des jours, voire des semaines, voire des mois, de temps supplémentaire pour commercialiser. Ou vous allez commercialiser vos produits sans services de sécurité, ce qui comporte son lot de risques (et de conséquences).
La collaboration dès le premier jour de développement est le moyen le plus efficace et le plus rapide de garantir que les applications sont disponibles, rapides et sécurisées lorsqu'elles sont mises en production.