Le sous-domaine d’administration n’arrête pas le CSRF : quatre vérifications avant la mise en ligne
Un nom d’hôte nouveau est une origine nouvelle, souvent le même site, donc les cookies same-site et les formulaires simples le traversent. Vérifiez les jetons, la porte et l’autorisation.
Déplacer l’interface d’administration sur admin.example.com est une vraie amélioration : cela garde les cookies d’administration loin des pages publiques, donne aux deux applications des caches séparés et permet d’appliquer une politique réseau plus stricte à un seul nom d’hôte. Ce n’est pas un contrôle CSRF, et cela ne rend pas le code du site public sûr à laisser ouvert devant un administrateur connecté. La séparation change ce que le navigateur considère comme une frontière, pas ce que le serveur vérifie, alors faites ces quatre vérifications avant de dire que c’est terminé.
Lisez origine et site comme deux mots différents
Une origine est schéma, hôte et port, donc https://www.example.com et https://admin.example.com sont des origines différentes. Un site, au sens SameSite, est le schéma plus le domaine enregistrable, donc ces deux noms d’hôte sont généralement le même site. Cette différence est tout le piège : l’attribut SameSite ne vous aidera pas entre sous-domaines frères, car le navigateur ne considère pas la requête comme inter-sites du tout. Pointez l’interface d’administration vers un domaine enregistrable réellement différent, par exemple admin-example.net, et la requête devient bien inter-sites, et c’est là que SameSite=Lax ou Strict commence à faire un travail sur lequel vous pouvez compter.
Testez-le dans le navigateur plutôt que de raisonner dessus.
Chargez le site public, ouvrez les outils de développement et regardez quels cookies le navigateur joint à une requête vers l’hôte d’administration. Si le cookie n’a pas d’attribut Domain, il est limité à l’hôte et reste sur son propre nom ; s’il porte Domain=example.com, il voyage vers chaque frère, et le nouveau sous-domaine ne vous a rien apporté pour ce cookie.
Séparez pouvoir lire la réponse de pouvoir envoyer la requête
La politique de même origine restreint la lecture des réponses entre origines. Elle n’empêche pas une requête d’être envoyée, et elle n’empêche pas un formulaire de porter des identifiants que le navigateur possède déjà. Une requête simple, avec un type de contenu de formulaire ou un GET simple, est livrée sans contrôle préalable ; CORS décide ensuite si le script appelant peut lire la réponse. Le CSRF vit entièrement du côté de l’envoi.
C’est pourquoi le serveur a besoin d’une vérification explicite pour les opérations qui changent l’état. Un jeton de synchronisation lié à la session, ou un cookie à double soumission comparé côté serveur, couvre le cas ; une comparaison Origin ou Referer mérite d’être ajoutée comme seconde porte mais pas comme la seule, car toute requête ne porte pas l’en-tête et tout proxy ne le conserve pas. Le guide OWASP de prévention du CSRF traite SameSite comme une couche parmi plusieurs, précisément pour cette raison.
Décidez ce que la porte réseau prouve réellement
Une liste d’exits VPN autorisés devant l’hôte d’administration est un bon contrôle et une faible affirmation d’identité. Elle prouve que la requête vient d’une adresse que le VPN attribue, qui peut être partagée par tous ceux sur cet exit, et elle ne dit rien de la personne ou de l’appareil derrière. Traitez-la comme un filtre d’entrée, non comme une authentification.
Gardez-la indépendante des vérifications au niveau de la requête. Si la liste est la seule chose entre un attaquant et un point de terminaison qui change l’état, une route mal configurée, un client en split-tunnel ou un portable emprunté supprime le contrôle entièrement.
Journalisez aussi les rejets hors des plages attendues. Une rafale de ceux-ci est le signal le plus utile que quelqu’un sonde la frontière que vous venez de construire, et le silence est le signal que le filtre n’est pas atteint du tout.
Gardez cookies, connexion et autorisation explicites
La portée du cookie décide de la majeure partie du résultat. Un cookie du domaine parent est envoyé à l’hôte d’administration, donc une injection de script sur le site public peut agir avec la session d’un administrateur ; un cookie limité à l’hôte ne le peut pas. Préférez la forme limitée à l’hôte, ajoutez l’attribut Secure et utilisez le préfixe __Host- là où la plateforme le prend en charge, car ce préfixe exige Secure, Path=/ et aucun attribut Domain. Lisez l’en-tête de réponse plutôt que le bocal à cookies : Set-Cookie: __Host-admin_session=...; Secure; Path=/; HttpOnly; SameSite=Lax, sans attribut Domain. Un navigateur écarte un cookie __Host- qui porte Domain, donc un cookie encore visible sur des hôtes frères prouve que le préfixe n’a jamais été appliqué.
Ensuite gardez l’autorisation sur le serveur, par requête, sur chaque point de terminaison d’administration, pas seulement à la connexion. Une requête venant d’un réseau autorisé doit encore être authentifiée et autorisée pour l’opération demandée ; le nom d’hôte n’est pas un acteur. Si les applications publique et d’administration partagent un flux de connexion ou un magasin de sessions, écrivez cette relation, car c’est le chemin le plus court d’un défaut du site public à une action d’administration.
Revérifiez la frontière après la séparation
Lancez les tests négatifs, pas seulement le chemin heureux : un formulaire inter-sites sans jeton doit être rejeté, un jeton d’une autre session doit être rejeté, et aucun GET ne doit changer l’état. Vérifiez les attributs des cookies dans les outils de développement, confirmez que la vérification Origin se déclenche sur une requête non concordante, et essayez l’hôte d’administration depuis l’extérieur de la plage VPN pour voir le filtre à l’œuvre.
Rien de tout cela n’élimine un défaut XSS à l’intérieur de l’application d’administration elle-même. Un nom d’hôte séparé est une frontière d’isolation côté navigateur avec une valeur réelle, et il ne remplace ni la prévention de l’exécution de code non sûr ni la protection des requêtes côté serveur. Après la séparation, la liste est plus courte qu’elle n’en a l’air : origine distincte, cookies limités à l’hôte, un jeton à chaque mutation, un filtre d’entrée auquel vous ne faites pas confiance comme identité, et une autorisation décidée par requête.