FormsAuthentication.SignOut () ne déconnecte pas l'utilisateur

143

Je me suis brisé la tête contre ça un peu trop longtemps. Comment empêcher un utilisateur de parcourir les pages d'un site après avoir été déconnecté à l'aide de FormsAuthentication.SignOut? Je m'attendrais à ce que cela le fasse:

FormsAuthentication.SignOut();
Session.Abandon();
FormsAuthentication.RedirectToLoginPage();

Mais ce n'est pas le cas. Si je tape directement une URL, je peux toujours accéder à la page. Je n'ai pas utilisé la sécurité roll-your-own depuis un moment, alors j'oublie pourquoi cela ne fonctionne pas.

Jason
la source
Ce code est très bien tel quel ... cliquer à nouveau dans le navigateur ne revisite pas la page sur le serveur, il recharge simplement la version locale en cache de la page. Toutes les solutions ci-dessous semblent ignorer ce fait et ne font rien de plus que ce que vous faites ici. En bref ... il n'y a pas de réponse à cette question qui résoudra l'utilisateur en regardant son cache à ce jour.Je ne pense pas qu'il existe un moyen de vider le cache dans say ... js ou avec une instruction côté serveur.
Guerre du
Cette réponse offre quelques moyens de vérifier, surtout si votre site échoue aux tests PEN: stackoverflow.com/questions/31565632/…
Tyler S. Loeper

Réponses:

211

Les utilisateurs peuvent toujours naviguer sur votre site Web car les cookies ne sont pas effacés lorsque vous appelez FormsAuthentication.SignOut()et ils sont authentifiés à chaque nouvelle demande. Dans la documentation MS, il est dit que le cookie sera effacé mais ce n'est pas le cas, bug? C'est exactement la même chose avec Session.Abandon(), le cookie est toujours là.

Vous devriez changer votre code pour ceci:

FormsAuthentication.SignOut();
Session.Abandon();

// clear authentication cookie
HttpCookie cookie1 = new HttpCookie(FormsAuthentication.FormsCookieName, "");
cookie1.Expires = DateTime.Now.AddYears(-1);
Response.Cookies.Add(cookie1);

// clear session cookie (not necessary for your current problem but i would recommend you do it anyway)
SessionStateSection sessionStateSection = (SessionStateSection)WebConfigurationManager.GetSection("system.web/sessionState");
HttpCookie cookie2 = new HttpCookie(sessionStateSection.CookieName, "");
cookie2.Expires = DateTime.Now.AddYears(-1);
Response.Cookies.Add(cookie2);

FormsAuthentication.RedirectToLoginPage();

HttpCookieest dans l' System.Webespace de noms. Référence MSDN .

Igor Jerosimić
la source
18
Cela fonctionne pour moi. Cependant, il convient de noter que si la propriété Domain a été définie sur le cookie FormsAuthentication lors de la connexion, elle devra également être définie lors de l'expiration du cookie lorsque vous vous déconnectez
Phil Hale
8
N'oubliez pas non plus cookie1.HttpOnly = true;
Dmitry Zaets
6
Cela me semble être une meilleure solution: Response.Cookies [FormsAuthentication.FormsCookieName] .Expires = DateTime.Now.AddDays (-1);
Randy H.
7
@RandyH. Le remplacement du cookie FormsAuthentication existant par un nouveau cookie vide garantit que même si le client remonte son horloge système, il ne pourra toujours pas récupérer les données utilisateur du cookie.
Tri Q Tran
9
Quelqu'un peut-il combiner tous ces commentaires dans la réponse?
David
22

L'utilisation de deux des publications ci-dessus par x64igor et Phil Haselden a résolu ce problème:

1. x64igor a donné l'exemple pour effectuer la déconnexion:

  • Vous devez d'abord effacer le cookie d'authentification et le cookie de session en renvoyant des cookies vides dans la réponse à la déconnexion.

    public ActionResult LogOff()
    {
        FormsAuthentication.SignOut();
        Session.Clear();  // This may not be needed -- but can't hurt
        Session.Abandon();
    
        // Clear authentication cookie
        HttpCookie rFormsCookie = new HttpCookie( FormsAuthentication.FormsCookieName, "" );
        rFormsCookie.Expires = DateTime.Now.AddYears( -1 );
        Response.Cookies.Add( rFormsCookie );
    
        // Clear session cookie 
        HttpCookie rSessionCookie = new HttpCookie( "ASP.NET_SessionId", "" );
        rSessionCookie.Expires = DateTime.Now.AddYears( -1 );
        Response.Cookies.Add( rSessionCookie );

2. Phil Haselden a donné l'exemple ci-dessus sur la façon d'empêcher la mise en cache après la déconnexion:

  • Vous devez invalider le cache côté client via la réponse .

        // Invalidate the Cache on the Client Side
        Response.Cache.SetCacheability( HttpCacheability.NoCache );
        Response.Cache.SetNoStore();
    
        // Redirect to the Home Page (that should be intercepted and redirected to the Login Page first)
        return RedirectToAction( "Index", "Home" ); 
    }
justdan23
la source
1
J'ai perdu toute la journée au travail pour résoudre ce problème. Une fois connecté, le bouton de déconnexion a commencé à appeler une action erronée dans le contrôleur (connexion et non déconnexion). Merci, cela a résolu le problème. Environnement de développement: ASP.NET 4.51 MVC 5.1
Ako
1
Bonne réponse! Humble suggestion: Utilisez la forma pour les cookies de session de compensation x64igor utilisé: SessionStateSection sessionStateSection = (SessionStateSection)WebConfigurationManager.GetSection("system.web/sessionState"); HttpCookie sessionCookie = new HttpCookie(sessionStateSection.CookieName, "");. En général, le nom du cookie de session ne l'est pas "ASP.NET_SessionId".
seebiscuit
20

Il me semble que vous n'avez pas configuré correctement votre section d'autorisation web.config. Voir ci-dessous pour un exemple.

<authentication mode="Forms">
  <forms name="MyCookie" loginUrl="Login.aspx" protection="All" timeout="90" slidingExpiration="true"></forms>
</authentication>
<authorization>
  <deny users="?" />
</authorization>
jwalkerjr
la source
C'est une solution beaucoup plus simple, je la marquerais comme une réponse. Comme j'ai une version de code fonctionnant sur différents serveurs sur un, je n'ai pas eu besoin de définir des propriétés supplémentaires que vous avez ajoutées ici et sur d'autres, je l'ai fait. La modification du code ne devrait donc pas être la bonne solution, la modification de la configuration est préférable.
Vladimir Bozic
Par défaut, slideExpiration est défini sur true ( msdn.microsoft.com/library/1d3t3c61(v=vs.100).aspx ). Et cela entraînera finalement l'invalidité du cookie après x minutes comme défini dans le délai d'expiration - et non lorsque l'utilisateur est déconnecté via SignOut (). Cela n'entraînera donc pas le comportement souhaité pour déconnecter un utilisateur à l'aide de FormsAuthentication. S'il vous plait corrigez moi si je me trompe.
OlafW
12

La clé ici est que vous dites "Si je saisis une URL directement ...".

Par défaut, sous l'authentification par formulaire, le navigateur met en cache les pages pour l'utilisateur. Ainsi, en sélectionnant une URL directement dans la liste déroulante de la zone d'adresse du navigateur, ou en la tapant, PEUT extraire la page du cache du navigateur et ne jamais revenir au serveur pour vérifier l'authentification / l'autorisation. La solution à cela consiste à empêcher la mise en cache côté client dans l'événement Page Load de chaque page ou dans OnLoad () de votre page de base:

Response.Cache.SetCacheability(HttpCacheability.NoCache);

Vous pourriez également souhaiter appeler:

Response.Cache.SetNoStore();
Phil Haselden
la source
11

J'ai aussi eu du mal avec ça.

Voici une analogie pour ce qui semble se passer ... Un nouveau visiteur, Joe, arrive sur le site et se connecte via la page de connexion en utilisant FormsAuthentication. ASP.NET génère une nouvelle identité pour Joe et lui donne un cookie. Ce cookie est comme la clé de la maison, et tant que Joe revient avec cette clé, il peut ouvrir la serrure. Chaque visiteur reçoit une nouvelle clé et une nouvelle serrure à utiliser.

Lorsqu'il FormsAuthentication.SignOut()est appelé, le système dit à Joe de perdre la clé. Normalement, cela fonctionne, puisque Joe n'a plus la clé, il ne peut pas entrer.

Cependant, si Joe revient un jour et qu'il a cette clé perdue, il est laissé entrer!

D'après ce que je peux dire, il n'y a aucun moyen de dire à ASP.NET de changer la serrure de la porte!

La façon dont je peux vivre avec cela est de me souvenir du nom de Joe dans une variable de session. Quand il se déconnecte, j'abandonne la session pour ne plus avoir son nom. Plus tard, pour vérifier s'il est autorisé à entrer, je compare simplement son Identity.Name à ce que la session actuelle a, et s'ils ne correspondent pas, il n'est pas un visiteur valide.

En bref, pour un site Web, ne vous fiez PAS User.Identity.IsAuthenticatedsans vérifier également vos variables de session!

Glen Little
la source
8
+ 1, je pense que cela s'appelle «Attaque de relecture de cookie». Il existe un article sur les limitations de FormsAuthentication.SignOut: support.microsoft.com/kb/900111
Dmitry
3
Pour quiconque cherche à suivre le lien ci-dessus, il est mort. Vous pouvez essayer d'utiliser le WaybackMachine pour obtenir une copie de cette page ici, mais il essaie IMMÉDIATEMENT de rediriger l'utilisateur. web.archive.org/web/20171128133421/https
//...
7

Après de nombreuses recherches, cela a finalement fonctionné pour moi. J'espère que cela aide.

public ActionResult LogOff()
{
    AuthenticationManager.SignOut();
    HttpContext.User = new GenericPrincipal(new GenericIdentity(string.Empty), null);
    return RedirectToAction("Index", "Home");
}

<li class="page-scroll">@Html.ActionLink("Log off", "LogOff", "Account")</li>
Khosro.Pakmanesh
la source
Je développe des applications web depuis des années en PHP. Donc je suis nouveau sur MVC ... J'avoue que j'adore ça, MAIS qui aurait pensé que quelque chose d'aussi simple que de déconnecter quelqu'un serait si difficile? J'ai essayé tous les autres scripts de cette page sur toute la ligne et c'est le seul qui a fonctionné. Merci d'avoir posté!
Anthony Griggs
6

Cela fonctionne pour moi

public virtual ActionResult LogOff()
    {
        FormsAuthentication.SignOut();
        foreach (var cookie in Request.Cookies.AllKeys)
        {
            Request.Cookies.Remove(cookie);
        }
        foreach (var cookie in Response.Cookies.AllKeys)
        {
            Response.Cookies.Remove(cookie);
        }
        return RedirectToAction(MVC.Home.Index());
    }
Korayem
la source
3

Le code que vous avez publié semble devoir supprimer correctement le jeton d'authentification par formulaire, il est donc possible que les dossiers / pages en question ne soient pas réellement protégés.

Avez-vous confirmé que les pages ne sont pas accessibles avant la connexion?

Pouvez-vous publier les paramètres web.config et le code de connexion que vous utilisez?

Abram Simon
la source
3

J'ai écrit une classe de base pour toutes mes pages et je suis arrivé au même problème. J'avais un code comme celui-ci et cela ne fonctionnait pas. En traçant, le contrôle passe de l'instruction RedirectToLoginPage () à la ligne suivante sans être redirigé.

if (_requiresAuthentication)
{
    if (!User.Identity.IsAuthenticated)
        FormsAuthentication.RedirectToLoginPage();

    // check authorization for restricted pages only
    if (_isRestrictedPage) AuthorizePageAndButtons();
}

J'ai découvert qu'il y avait deux solutions. Soit pour modifier FormsAuthentication.RedirectToLoginPage (); être

if (!User.Identity.IsAuthenticated)
    Response.Redirect(FormsAuthentication.LoginUrl);

OU pour modifier le web.config en ajoutant

<authorization>
  <deny users="?" />
</authorization>

Dans le second cas, lors du traçage, le contrôle n'a pas atteint la page demandée. Il a été redirigé immédiatement vers l'URL de connexion avant d'atteindre le point d'arrêt. Par conséquent, la méthode SignOut () n'est pas le problème, la méthode de redirection est celle-là.

J'espère que cela peut aider quelqu'un

Cordialement

Wahid Shalaly
la source
2
Vous pouvez également appeler Response.End () juste après avoir appelé FormsAuthentication.RedirectToLoginPage ()
murki
Je pense qu'il y a un peu de mauvaise communication de la part de MS. Vous devez verrouiller les personnes si vous souhaitez qu'elles reviennent à la page de connexion. Sinon, le framework vous autorisera joyeusement l'accès. Vous devez donc dire la solution n ° 2 dans ce post.
Josh Robinson
3

J'ai juste essayé quelques-unes des suggestions ici et même si j'ai pu utiliser le bouton de retour du navigateur, lorsque j'ai cliqué sur une sélection de menu, le jeton [Autoriser] pour cet [ActionResult] m'a renvoyé directement à l'écran de connexion.

Voici mon code de déconnexion:

        FormsAuthentication.SignOut();
        Response.Cookies.Remove(FormsAuthentication.FormsCookieName);
        Response.Cache.SetExpires(DateTime.Now.AddSeconds(-1));
        HttpCookie cookie = HttpContext.Request.Cookies[FormsAuthentication.FormsCookieName];
        if (cookie != null)
        {
            cookie.Expires = DateTime.Now.AddDays(-1);
            Response.Cookies.Add(cookie);
        }

Bien que la fonction de retour sur le navigateur me ramène et affiche le menu sécurisé (je travaille toujours là-dessus), je n'ai pas pu faire quoi que ce soit de sécurisé dans l'application.

J'espère que cela t'aides

DonH
la source
Merci. C'est la solution qui a fonctionné pour moi (pas besoin de <deny users="?" />web.config)
Alexei
3

J'ai essayé la plupart des réponses dans ce fil, pas de chance. J'ai fini avec ceci:

protected void btnLogout_Click(object sender, EventArgs e)
{
    FormsAuthentication.Initialize();
    var fat = new FormsAuthenticationTicket(1, "", DateTime.Now, DateTime.Now.AddMinutes(-30), false, string.Empty, FormsAuthentication.FormsCookiePath);
    Response.Cookies.Add(new HttpCookie(FormsAuthentication.FormsCookieName, FormsAuthentication.Encrypt(fat)));
    FormsAuthentication.RedirectToLoginPage();
}

Je l'ai trouvé ici: http://forums.asp.net/t/1306526.aspx/1

stoffen
la source
3

Cette réponse est techniquement identique à Khosro.Pakmanesh. Je la poste pour clarifier en quoi sa réponse diffère des autres réponses sur ce fil, et dans quel cas d'utilisation elle peut être utilisée.

En général, pour effacer une session utilisateur,

HttpContext.Session.Abandon();
FormsAuthentication.SignOut();

déconnectera effectivement l'utilisateur. Cependant , si dans la même demande vous devez vérifier Request.isAuthenticated(comme cela peut souvent arriver dans un filtre d'autorisation, par exemple), vous constaterez que

Request.isAuthenticated == true

même _après vous l'avez fait HttpContext.Session.Abandon()et FormsAuthentication.SignOut().

La seule chose qui fonctionnait était de faire

AuthenticationManager.SignOut();
HttpContext.User = new GenericPrincipal(new GenericIdentity(string.Empty), null);

Cela définit effectivement Request.isAuthenticated = false.

voirbiscuit
la source
2

Cela a commencé à m'arriver lorsque j'ai défini la propriété authentication> forms> Path dans Web.config. La suppression de cela a résolu le problème, et un simple a de FormsAuthentication.SignOut();nouveau supprimé le cookie.

BPM
la source
1

Il se peut que vous vous connectiez à partir d'un sous-domaine (sub1.domain.com) et que vous essayez de vous déconnecter d'un autre sous-domaine (www.domain.com).

jorsh1
la source
1

J'ai juste eu le même problème, où SignOut () n'a apparemment pas réussi à supprimer correctement le ticket. Mais seulement dans un cas spécifique, où une autre logique a provoqué une redirection. Après avoir supprimé cette deuxième redirection (remplacée par un message d'erreur), le problème a disparu.

Le problème doit avoir été que la page a été redirigée au mauvais moment, ne déclenchant donc pas l'authentification.

Peder Skou
la source
1

J'ai un problème similaire maintenant et je pense que le problème dans mon cas ainsi que dans l'affiche originale est dû à la redirection. Par défaut, un Response.Redirect provoque une exception qui bouillonne immédiatement jusqu'à ce qu'elle soit interceptée et que la redirection soit immédiatement exécutée, je suppose que cela empêche la collecte de cookies modifiée d'être transmise au client. Si vous modifiez votre code pour utiliser:

Response.Redirect("url", false);

Cela empêche l'exception et semble permettre au cookie d'être correctement renvoyé au client.

Lostatredrock
la source
1

Essayez simplement d'envoyer une variable de session lorsque vous appuyez sur Connexion. Et sur la page d'accueil, vérifiez d'abord si cette session est vide comme ceci dans le chargement de la page ou dans l'événement Init:

if(Session["UserID"] == null || Session["UserID"] == "")
{
    Response.Redirect("Login.aspx");
}
Devrishi
la source
1

Pour moi, l'approche suivante fonctionne. Je pense que s'il y a une erreur après l'instruction "FormsAuthentication.SignOut ()", SingOut ne fonctionne pas.

public ActionResult SignOut()
    {
        if (Request.IsAuthenticated)
        {
            FormsAuthentication.SignOut();

            return Redirect("~/");
        }
        return View();
     }
Aji
la source
0

Testez-vous / voyez-vous ce comportement à l'aide d'IE? Il est possible que IE serve ces pages à partir du cache. Il est notoirement difficile d'amener IE à vider son cache, et donc à de nombreuses reprises, même après vous être déconnecté, taper l'url de l'une des pages "sécurisées" afficherait le contenu mis en cache d'avant.

(J'ai vu ce comportement même lorsque vous vous connectez en tant qu'utilisateur différent, et IE affiche la barre "Bienvenue" en haut de votre page, avec le nom d'utilisateur de l'ancien utilisateur. De nos jours, un rechargement le mettra à jour, mais s'il persiste , cela pourrait encore être un problème de mise en cache.)

Stobor
la source
0

Faire Session.abandon () et détruire le cookie fonctionne plutôt bien. J'utilise mvc3 et il semble que le problème se produit si vous accédez à une page protégée, que vous vous déconnectez et que vous accédez à l'historique de votre navigateur. Pas un gros problème mais toujours un peu ennuyeux.

Essayer de parcourir des liens sur mon application Web fonctionne de la bonne manière.

Le configurer pour ne pas faire de mise en cache du navigateur peut être la voie à suivre.

James
la source
0

Pour MVC, cela fonctionne pour moi:

        public ActionResult LogOff()
        {
            FormsAuthentication.SignOut();
            return Redirect(FormsAuthentication.GetRedirectUrl(User.Identity.Name, true));
        }
Anovo
la source
0

Je voulais ajouter quelques informations pour aider à comprendre le problème. L'authentification par formulaire permet de stocker les données utilisateur soit dans un cookie, soit dans la chaîne de requête de l'URL. La méthode prise en charge par votre site peut être configurée dans le fichier web.config.

Selon Microsoft :

La méthode SignOut supprime les informations de ticket d'authentification par formulaire du cookie ou de l'URL si CookiesSupported est false .

En même temps, disent-ils :

Une des valeurs HttpCookieMode qui indique si l'application est configurée pour l'authentification par formulaire sans cookie. La valeur par défaut est UseDeviceProfile .

Enfin, concernant UseDeviceProfile, ils disent :

Si la propriété CookieMode est définie sur UseDeviceProfile, la propriété CookiesSupported retournera true si le navigateur de la demande actuelle prend en charge à la fois les cookies et la redirection avec des cookies ; sinon, la propriété CookiesSupported renverra false.

Assemblant tout cela ensemble, selon le navigateur de l'utilisateur, la configuration par défaut peut entraîner CookiesSupported être vrai , ce qui signifie que la méthode SignOut n'efface pas le billet du cookie. Cela semble contre-intuitif et je ne sais pas pourquoi cela fonctionne de cette façon - je m'attendrais à ce que SignOut déconnecte réellement l'utilisateur en toutes circonstances.

Une façon de faire fonctionner SignOut par lui-même est de changer le mode cookie en "UseCookies" (c'est-à-dire que les cookies sont requis) dans le fichier web.config:

<authentication mode="Forms">
  <forms loginUrl="~/Account/SignIn" cookieless="UseCookies"/>
</authentication>

Selon mes tests, cela permet à SignOut de fonctionner par lui-même au détriment de votre site nécessitant désormais des cookies pour fonctionner correctement.

RogerMKE
la source
Je pense que vous lisez mal. En ce qui concerne SignOut (), je suis à peu près sûr que ce qu'ils veulent dire, c'est qu'il sera effacé de l'URL si CookiesSupported est faux, sinon du cookie. C'est-à-dire qu'ils auraient dû écrire "La méthode SignOut supprime les informations de ticket d'authentification par formulaire du cookie ou, si CookiesSupported est faux, de l'URL."
Oskar Berggren
-1

Sachez que WIF refuse de dire au navigateur de nettoyer les cookies si le message wsignoutcleanup de STS ne correspond pas à l'url avec le nom de l'application d'IIS, et je veux dire CASE SENSITIVE . WIF répond avec la vérification verte OK, mais ne le pas n'enverra la commande de suppression des cookies au navigateur.

Vous devez donc faire attention à la sensibilité à la casse de votre URL.

Par exemple, ThinkTecture Identity Server enregistre les URL des RP visiteurs dans un cookie, mais il les rend toutes en minuscules. WIF recevra le message wsignoutcleanup en minuscules et le comparera avec le nom de l'application dans IIS. S'il ne correspond pas, il ne supprime aucun cookie, mais signale OK au navigateur. Donc, pour ce serveur d'identité, j'avais besoin d'écrire toutes les URL dans web.config et tous les noms d'application dans IIS en minuscules, afin d'éviter de tels problèmes.

N'oubliez pas non plus d'autoriser les cookies tiers dans le navigateur si vous avez des applications en dehors du sous-domaine de STS, sinon le navigateur ne supprimera pas les cookies même si WIF le lui dit.

Stefan
la source
1
WIF? STS? ThinkTecture Identity Server? Que sont toutes ces choses et comment se rapportent-elles à cette question?
Oskar Berggren