Les pages SaaS publiques peuvent rester ouvertes à tous tandis que les opérations d’administration qui se trouvent derrière refusent quiconque est hors du VPN. Le mécanisme qui rend cela possible n’est ni un menu caché ni une page de connexion séparée ; c’est un contrôle du chemin réseau qui a livré la requête. Si la preuve d’adresse est fausse, le contrôle soit exclut de vrais administrateurs, soit admet quiconque sait taper un en-tête. Commencez par ce que l’origine reçoit réellement.

Ce que l’origine reçoit réellement après le tunnel

Lorsqu’un navigateur atteint le site public via un VPN qui transporte aussi le trafic internet, la passerelle traduit la connexion avant qu’elle ne parte. Le SaaS voit l’adresse de sortie publique de la passerelle, généralement une adresse ou une petite plage, et ne voit jamais l’adresse du tunnel du portable. C’est pourquoi ajouter 10.90.0.42 à la liste d’autorisation échoue, et pourquoi ajouter tout le RFC 1918 échoue encore plus fort : le second admet tous les réseaux privés qui peuvent vous atteindre. La seule adresse qui mérite une condition est celle que votre organisation possède réellement à la sortie.

curl -s https://ifconfig.me
dig +short admin.example.com

Le tunneling divisé casse cette hypothèse en silence. Si le VPN ne transporte que des routes de bureau privées, la requête vers le nom d’hôte d’administration public sort par le fournisseur local et l’origine voit une adresse résidentielle. Une icône VPN connectée ne prouve rien sur cette destination précise, alors testez la destination et non le tunnel.

Arrêtez-vous et corrigez le routage avant d’écrire la moindre politique si l’adresse de sortie varie selon l’utilisateur. Une condition qui ne correspond qu’à certains administrateurs est pire qu’aucune condition, car elle cache le problème jusqu’à un incident.

Ne faites confiance qu’au proxy qui réécrit l’adresse

La plupart des sites publics se trouvent derrière un CDN ou un proxy inverse, donc l’adresse de socket de l’origine appartient à ce frontal et non au visiteur. Le frontal transmet généralement l’adresse d’origine dans un en-tête, et cet en-tête est contrôlé par un attaquant, sauf si l’origine ne l’accepte que de proxys connus. Configurez explicitement la liste des sources de confiance et rendez l’origine inaccessible depuis tout autre endroit. Sans cette seconde moitié, un attaquant contourne le proxy et envoie l’en-tête directement.

set_real_ip_from 192.0.2.0/24;
real_ip_header X-Forwarded-For;
real_ip_recursive on;
curl -H 'X-Forwarded-For: 198.51.100.27' https://origin.example.com/admin

Le même raisonnement s’applique au protocole PROXY : il n’est digne de confiance que lorsque l’écouteur est lié à une interface que le proxy peut atteindre et que rien d’autre ne peut atteindre. Un contexte d’adresse manquant doit faire échouer le contrôle protégé plutôt que de retomber sur une autorisation par défaut. Un en-tête absent n’est pas la preuve d’un chemin de confiance.

Attachez la condition à chaque opération protégée

Placez le contrôle à la frontière d’autorisation que traverse chaque opération protégée, pas dans la page qui rend le menu. Les groupes de routes, un middleware ou une couche de politique fonctionnent tous ; une copie par gestionnaire non, car le gestionnaire que quelqu’un ajoute au prochain trimestre ne l’aura pas.

user = requireAuthenticatedSession()
requirePermission(user, "admin")
requireVpnEgress(verifiedClientAddress(request))
performProtectedOperation(user, request)

La même frontière doit couvrir l’API JSON, les exports, les URL de téléchargement signées et la poignée de main WebSocket, car aucune d’elles n’a besoin que la page HTML soit demandée d’abord. Un lien de téléchargement créé pendant que le VPN était actif reste valide ensuite, sauf si le point de téléchargement revérifie. Les sessions de longue durée ont aussi besoin d’une règle de réévaluation, sinon une requête authentifiée devient une exemption permanente. Le refus par défaut est le seul réglage qui survit au renouvellement du personnel.

Les caches partagés sont l’autre façon de contourner le contrôle. Un cache qui contient déjà une réponse protégée peut la servir sans contacter l’origine, donc marquez les réponses d’administration comme privées et gardez-les hors du stockage partagé. Si un cache doit les servir, le contrôle d’accès doit s’exécuter avant le service, ce qui signifie généralement qu’il ne peut pas du tout être un cache partagé.

Testez les contournements que vous venez de fermer

Exercez la matrice plutôt que le chemin heureux : un administrateur via le VPN, le même administrateur hors de celui-ci, un non-administrateur via celui-ci, et un en-tête d’adresse falsifié. Ajoutez une requête directe vers l’origine et une requête avec réponse en cache, car ce sont ces deux-là qui sautent entièrement le frontal.

curl -sI https://admin.example.com/export.csv
curl -s -H 'X-Forwarded-For: 198.51.100.27' https://origin.example.com/admin
curl -s https://admin.example.com/api/me -o /dev/null -w '%{http_code}\n'

Chaque requête doit renvoyer le statut que la politique promet, et le journal doit consigner quelle preuve d’adresse a été utilisée. Couvrez IPv6 séparément, car une origine à double pile peut voir une famille de sources différente de celle testée. Gardez les opérations client ordinaires fonctionnelles hors du VPN, sinon le changement sera annulé à la première plainte d’un client. Écrivez le résultat attendu de chaque ligne avant de l’exécuter, afin qu’un succès surprenant soit visible.

L’adresse de sortie prouve un chemin réseau autorisé, pas une personne ni un appareil géré. Gardez-la comme une condition à côté de l’authentification et de la permission, et la zone d’administration reste étroite sans transformer tout le produit en service réservé au VPN. Cette séparation est la conception, pas une limitation de celle-ci.

Articles similaires