Sous-domaines partagés, deux entrées : qui voit quels dossiers ?
Le sous-domaine choisit l’image de marque ; l’appartenance vérifiée choisit les données. Exécutez les mêmes contrôles de locataire, que la requête arrive depuis une adresse de bureau ou via un VPN.
La Clinique A et la Clinique B partagent une base de code, deux sous-domaines et un mélange de visiteurs au bureau et via VPN. Les équipes réseau ont tendance à configurer les deux entrées séparément, et c’est très bien, tant que l’application ne le fait pas. Les contrôles ci-dessous appartiennent à l’application et doivent se comporter à l’identique, quelle que soit la porte utilisée par la requête.
Premier point : le nom d’hôte ne nomme qu’un locataire
Résolvez le nom d’hôte demandé par rapport à une correspondance tenue par l’administrateur, et rejetez tout ce qui est inconnu. Utilisez la valeur d’hôte validée par le cadre plutôt que l’en-tête brut, afin qu’un Host fabriqué ne puisse pas injecter une clé de recherche. Une correspondance réussie indique au code quelle configuration de locataire et quelle image de marque charger. Elle ne dit rien sur la possibilité, pour l’appelant, de voir les données de ce locataire. Gardez cette seconde question dans une fonction distincte avec une entrée distincte.
Une fonction d’aide partagée qui renvoie à la fois le contexte et l’autorisation à partir d’une seule recherche de nom d’hôte est le début du défaut.
Deuxième point : l’appartenance vient de la session
Recherchez les appartenances actives de l’utilisateur authentifié et comparez-les au locataire du premier contrôle. Un utilisateur qui appartient aux deux cliniques peut changer, mais le changement doit être une action explicite enregistrée dans le contexte de la requête, et non une déduction tirée de l’URL. Un utilisateur qui appartient à une clinique et demande l’autre est refusé, y compris pour les téléchargements et les appels d’API.
Testez le cas négatif en modifiant un identifiant de dossier et un paramètre de locataire. Si l’une ou l’autre modification renvoie des données, la portée est lue depuis la requête plutôt que depuis l’appartenance vérifiée.
La portée des cookies et l’authentification unique décident où un compte peut être présenté. Elles ne décident pas de ce que ce compte peut lire.
Troisième point : le réseau ne prouve rien sur la personne
Une passerelle VPN authentifie un tunnel, et une liste d’adresses autorisées publique authentifie une plage d’adresses. Ni l’une ni l’autre n’identifie une personne. Le NAT regroupe de nombreux utilisateurs sur une seule adresse source, et une plage de bureau fait de même pour tous les occupants du bâtiment. Traitez les deux comme des faits de transport et gardez l’autorisation dans l’application, ou dans une intégration qui affirme explicitement l’identité et est examinée comme telle.
DNS est un troisième fait de transport. Une réponse privée exige une route privée, et un front-end public exige un chemin d’origine pris en charge. Aucun d’eux ne porte d’autorisation.
Une phrase à garder au mur : le réseau décide comment une requête arrive, et l’application décide de ce qu’elle peut lire.
Quatrième point : exécutez la matrice sur les deux entrées
Utilisez des comptes contrôlés pour exercer A sur A, B sur B, A sur B, un nom d’hôte inconnu et un compte authentifié sans appartenance. Le quatrième et le cinquième doivent échouer en refusant, sans jamais se rabattre sur un locataire. Exécutez la même matrice par l’entrée VPN également, avec un pot de session par compte :
curl -s -o /dev/null -w '%{http_code}' -b jar.txt https://clinic-b.example.com/records/42
curl -s -o /dev/null -w '%{http_code}' -b jar.txt https://clinic-a.example.com/records/42
Modifiez ensuite une entrée de la liste d’adresses autorisées et relancez. Un résultat qui bouge signifie qu’un contrôle réseau prend une décision d’autorisation.
Répétez la matrice après tout changement de front-end, car un second chemin de code qui contourne le contrôle est la façon la plus courante dont cette frontière s’érode. Le résultat attendu est ennuyeux : chaque entrée produit les mêmes réponses.