Vulnérabilité de redirection ouverte dans les callbacks d'authentification : comment la corriger
Comprenez comment une redirection ouverte se produit dans les callbacks d'authentification, comment les attaquants l'exploitent, et comment corriger les redirections avec des listes d'autorisation strictes et une analyse sûre des URL.

Pourquoi les redirections dans les flux de connexion peuvent devenir une faille de sécurité
Une redirection est une instruction qui dit au navigateur « allez sur cette autre page ». Dans les flux de connexion, les redirections servent à rendre l'expérience fluide : votre app envoie quelqu'un se connecter, puis le ramène vers ce qu'il essayait de voir.
L'étape « ramenez-moi » est généralement portée dans un paramètre comme returnTo, next, ou redirect. Si votre app accepte n'importe quelle URL à cet endroit, vous avez une vulnérabilité de redirection ouverte.
On retrouve souvent ça dans des prototypes parce que les redirections donnent un résultat immédiat pour une démo fluide. Le code généré par l'IA ou écrit à la hâte privilégie souvent « que ça marche » et saute les règles de sécurité ennuyeuses.
Le risque dépasse le simple fait que « l'utilisateur arrive sur la mauvaise page ». Votre domaine devient un point de départ de confiance pour envoyer des gens vers un site contrôlé par un attaquant. Le lien semble légitime parce qu'il commence sur votre vrai domaine et présente souvent un écran de connexion familier.
Un scénario de phishing réaliste ressemble à ceci : quelqu'un clique sur un lien « Se connecter » dans un e-mail ou un chat, arrive sur votre vraie page de connexion, se connecte, puis est redirigé vers un faux écran convaincant qui lui demande de « se reconnecter », d'entrer un code MFA ou de confirmer des renseignements de paiement. Beaucoup d'utilisateurs ne remarquent rien parce que tout a l'air normal jusqu'à l'étape finale.
Ce que signifient les redirections ouvertes et les callbacks d'authentification
Une redirection ouverte se produit lorsque votre application laisse une entrée non fiable décider où l'utilisateur est envoyé ensuite. L'exemple classique est une URL comme https://yourapp.com/redirect?to=..., où to peut être n'importe quel site.
Un callback d'authentification est la page vers laquelle votre fournisseur d'identité renvoie après la connexion. Après qu'un utilisateur se connecte avec Google, GitHub ou un autre fournisseur, le fournisseur le renvoie à votre app à une URL de callback afin que votre app termine la connexion et crée une session.
Le problème commence quand vous combinez les deux.
Un scénario courant :
- Un utilisateur essaie d'accéder à
/billing. - Votre app l'envoie vers
/login?next=/billing. - Après la connexion, votre callback lit
next(oureturnUrl,redirect,continue) et redirige l'utilisateur là-bas.
Si le callback accepte next=https://evil.example, vous avez introduit une redirection ouverte dans la partie la plus fiable de votre produit.
L'impact ne se limite pas au phishing. Les redirections dans les flux OAuth peuvent aussi augmenter la portée des dégâts quand les équipes passent des valeurs sensibles via les URL pendant les premiers développements. Même quand vous ne faites que retransmettre un code OAuth, vous pouvez divulguer des données via l'historique du navigateur, les logs, les en-têtes referrer, ou simplement en amenant l'utilisateur sur une page conçue pour le tromper et lui faire céder l'accès.
Schémas de redirection risqués courants à rechercher
La plupart des bugs de redirection d'auth commencent de la même manière : une équipe veut « revenir là où vous étiez », donc elle passe un paramètre de redirection et le traite comme sûr.
Signes alarmants à chercher :
- Tout paramètre de requête qui contrôle la navigation après la connexion (
next,returnTo,redirect,url) utilisé directement dans un redirect HTTP ouwindow.location. - Du code qui accepte des URL complètes (
https://example.com) au lieu de chemins internes (/dashboard). - Des cibles de redirection venant de localStorage, cookies ou en-têtes et traitées comme fiables.
- Une « validation » qui ne vérifie que
startsWith('/'). - Plusieurs étapes de décodage/normalisation qui rendent flou ce qui a été validé par rapport à ce qui est effectivement utilisé.
Deux cas limites piègent beaucoup d'équipes :
Protocol-relative URLs : des valeurs comme //evil.com ressemblent à un chemin, mais les navigateurs les traitent comme « utiliser le schéma actuel et aller sur evil.com ». Un simple contrôle startsWith('/') laisse passer cela.
URLs encodées : les attaquants peuvent cacher la même astuce dans l'encodage. %2F%2Fevil.com devient //evil.com après décodage. Si vous validez avant de décoder, ou si vous décodez plusieurs fois à différents endroits, vous pouvez approuver une chaîne et rediriger vers une autre.
Comment les attaquants exploitent réellement les redirections ouvertes
Les attaquants aiment les redirections ouvertes parce qu'ils peuvent « emprunter » la confiance liée à votre domaine. La victime voit votre site réel dans la barre d'adresse, se connecte, et n'est envoyée vers quelque chose de malveillant qu'à la fin.
Une attaque très courante :
-
L'attaquant partage un lien vers votre vrai domaine qui inclut un paramètre de redirection, par exemple
?next=https://evil.example. -
Votre app affiche la vraie page de connexion.
-
Après la connexion, votre app redirige l'utilisateur vers le site de l'attaquant.
-
Le site de l'attaquant affiche un message crédible de type « session expirée » ou « confirmez votre compte » et capture des identifiants ou des codes MFA.
OAuth peut aggraver le problème si votre endpoint de callback échange ou traite des codes/tokens puis redirige immédiatement en se basant sur une entrée contrôlée par l'utilisateur. Même si les données sont de courte durée, une fenêtre courte peut suffire.
Un exemple réaliste : le lien de connexion qui envoie les utilisateurs ailleurs
Un prototype ajoute souvent un paramètre returnTo pour que la connexion paraisse soignée.
Une URL normale pourrait être :
/login?returnTo=/billing
Le bug apparaît quand returnTo est traité comme « n'importe quelle URL » au lieu de « un chemin sûr à l'intérieur de notre app ».
Ça marche alors aussi :
/login?returnTo=https://attacker.example/fake-dashboard
Rien ne casse. L'utilisateur se connecte avec succès, puis atterrit sur un site qui ressemble à votre produit mais qui ne l'est pas. Du point de vue de l'utilisateur, votre connexion a fonctionné, donc l'écran suivant paraît digne de confiance.
La leçon est simple : une fonctionnalité « revenir là où vous étiez » ne doit accepter que des destinations sûres et attendues. Si elle peut pointer vers une URL externe, c'est une porte ouverte.
Le modèle plus sûr : chemins relatifs et listes d'autorisation strictes
L'approche la plus sûre est volontairement ennuyeuse : traitez la destination post-connexion comme un chemin interne, pas comme une URL complète.
Règle 1 : n'acceptez que des chemins relatifs, pas des URL complètes
N'acceptez que des valeurs comme /settings ou /billing. Évitez d'accepter https://... et rejetez explicitement les valeurs protocol-relative comme //....
Une base utile : exiger un / initial simple, et rejeter tout ce qui commence par //.
Règle 2 : validez par rapport à une liste d'autorisation stricte
Même si vous n'acceptez que des chemins relatifs, vous pouvez vouloir restreindre encore où les utilisateurs peuvent atterrir après l'auth. Une liste d'autorisation empêche des destinations embarrassantes ou risquées comme des boucles /logout, des routes qui déclenchent des actions sensibles, ou des pages réservées à certains rôles.
Gardez-la petite. Autorisez quelques routes connues et sûres (ou quelques préfixes sûrs) et redirigez tout le reste vers une page sûre par défaut comme /dashboard.
Normaliser et analyser avant de décider
Normalisez l'entrée une fois : supprimez les espaces et décodez l'encodage percent une seule fois. Puis validez le chemin résultant. Évitez de décoder deux fois ou de valider sur une représentation différente de celle qui sera réellement utilisée pour la redirection.
Faites en sorte que les échecs soient ennuyeux
Si la valeur est absente ou invalide, ignorez-la et redirigez vers une destination sûre connue. Journalisez les rejets pour repérer les sondages et le code client défaillant.
Étape par étape : corriger la gestion des redirections dans un prototype
La logique de redirection a tendance à être répartie entre les middleware, les handlers de callback et le code UI. Le moyen le plus rapide de la sécuriser est de considérer chaque destination de redirection comme une entrée non fiable et de centraliser la validation.
-
Faites l'inventaire de chaque source de redirection : paramètres de requête (
next,returnTo,redirect,callback,continue), cookies, localStorage, et tout middleware d'auth qui « se souvient » d'où l'utilisateur venait. -
Choisissez votre règle : pour la plupart des apps, n'acceptez que des chemins relatifs. Si vous avez vraiment besoin de redirections externes (rare), n'autorisez que une courte liste d'origines exactes que vous contrôlez.
-
Normalisez une seule fois : coupez les espaces, décodez une fois, et rejetez les caractères de contrôle.
-
Validez strictement :
- Exigez un
/initial simple. - Rejetez
//, tout schéma commehttp:oujavascript:, et les barres obliques inverses (\\\\) y compris les barres encodées. - Rejetez la traversée de répertoires comme
..et les octets nuls. - Si vous autorisez des URL complètes, exigez une correspondance d'origine exacte par rapport à votre liste d'autorisation.
- Redirigez et journalisez : en cas d'échec, envoyez l'utilisateur vers une valeur par défaut sûre et enregistrez la valeur rejetée.
Erreurs courantes qui maintiennent la vulnérabilité
La plupart des corrections qui échouent semblent « validées » mais continuent de traiter les redirections comme des chaînes brutes.
Pièges fréquents :
- Liste d'autorisation par sous-chaîne (par exemple, vérifier
includes('mydomain.com')). Les attaquants peuvent utilisermydomain.com.evil.comou cacher du texte de confiance dans le chemin/la query. - Validation uniquement côté client. Les vérifs côté client améliorent l'UX, mais le serveur doit être la porte finale.
- Valider un paramètre mais rediriger avec un autre à cause d'aides du framework ou de la priorité des paramètres.
- Normaliser de façon incohérente, valider avant de décoder ou décoder plusieurs fois.
Faites aussi attention à « on l'a défini plus tôt, donc c'est fiable ». Si une valeur est stockée dans localStorage, un champ caché ou un cookie, l'attaquant peut toujours la modifier ou contourner la page qui l'a définie.
Vérifications rapides avant la mise en production
Vous pouvez détecter la plupart des problèmes de redirection avec quelques tests ciblés. L'objectif est simple : aucune valeur contrôlée par l'utilisateur ne doit jamais envoyer un navigateur vers un domaine inattendu, et les valeurs inconnues doivent atterrir quelque part de sûr.
Testez le paramètre que votre app utilise après la connexion (next, redirect, returnTo, callbackUrl). Confirmez qu'un chemin interne normal fonctionne, puis essayez des entrées qui passent souvent les contrôles naïfs :
https://example.com(doit être rejeté)//evil.comet%2F%2Fevil.com(doivent être rejetés)\\\\evil.com(certains frameworks normalisent ça de façon surprenante)- Une route interne inconnue comme
/definitely-not-real(doit revenir à une valeur par défaut sûre)
Répétez les mêmes tests sur le code de routage client et sur les endpoints serveur qui terminent les sessions ou gèrent les callbacks OAuth. Les attaquants utiliseront le chemin le plus faible.
Étapes suivantes : parvenir à une configuration de redirection propre et sûre
Les bugs de redirection ouverte n'habitent rarement un seul endroit. Dans les prototypes, ils apparaissent partout où l'app essaie d'être utile après la connexion : guards de routes, middleware qui renvoie les utilisateurs non authentifiés, handlers de callback OAuth, liens d'invitation et flux d'onboarding.
Un bon état final est ennuyeux : chaque redirection est soit un chemin relatif connu et sûr, soit (si vous en avez vraiment besoin) une URL absolue qui correspond à une courte liste d'autorités que vous contrôlez. Tout le reste est ignoré et remplacé par une valeur par défaut sûre.
Si vous traitez un code généré par l'IA et que vous voulez une seconde paire d'yeux, FixMyMess (fixmymess.ai) se concentre sur le diagnostic et la réparation de ce type de problèmes d'auth et de redirection, ainsi que des problèmes connexes comme des secrets exposés et des patterns non sûrs qui fonctionnent en démo mais échouent en production.