FormsAuthentication.SignOut () no cierra la sesión del usuario

143

Golpeé mi cabeza contra esto demasiado tiempo. ¿Cómo evito que un usuario explore las páginas de un sitio después de haber cerrado sesión con FormsAuthentication.SignOut? Esperaría que esto lo haga:

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

Pero no lo hace. Si escribo una URL directamente, aún puedo navegar a la página. No he usado la seguridad de rodar tu propio tiempo, así que olvido por qué esto no funciona.

Jason
fuente
Ese código está bien como está ... al volver a hacer clic en el navegador no se vuelve a visitar la página en el servidor, simplemente recarga la versión local en caché de la página. Todas las soluciones a continuación parecen ignorar ese hecho y en realidad no hacen nada más de lo que están haciendo aquí. En resumen ... no hay respuesta a esta pregunta que resuelva al usuario mirando su caché hasta la fecha. No creo que haya una manera de borrar el caché en say ... js o con una instrucción del lado del servidor.
Guerra
Esta respuesta ofrece algunas formas de verificar, especialmente si su sitio está fallando las pruebas PEN: stackoverflow.com/questions/31565632/…
Tyler S. Loeper el

Respuestas:

211

Los usuarios aún pueden navegar por su sitio web porque las cookies no se borran cuando llama FormsAuthentication.SignOut()y se autentican en cada nueva solicitud. En la documentación de MS se dice que la cookie se borrará pero no, ¿error? Es exactamente lo mismo con Session.Abandon(), la cookie sigue ahí.

Debe cambiar su código a esto:

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á en el System.Webespacio de nombres. Referencia de MSDN .

Igor Jerosimić
fuente
18
Esto funciona para mi. Sin embargo, vale la pena señalar que si la propiedad de dominio se ha configurado en la cookie de autenticación de formularios al iniciar sesión, también deberá configurarse al expirar la cookie cuando cierre la sesión
Phil Hale
8
Tampoco olvides cookie1.HttpOnly = true;
Dmitry Zaets
66
Esto parece una mejor solución para mí: Response.Cookies [FormsAuthentication.FormsCookieName] .Expires = DateTime.Now.AddDays (-1);
Randy H.
77
@RandyH. Anular la cookie de autenticación de formularios existente con una nueva cookie vacía garantiza que incluso si el cliente retrasa su reloj del sistema, aún no podrá recuperar ningún dato de usuario de la cookie.
Tri Q Tran
9
¿Alguien puede combinar todos estos comentarios en la respuesta?
David
22

Usando dos de las publicaciones anteriores por x64igor y Phil Haselden resolvió esto:

1. x64igor dio el ejemplo para cerrar la sesión:

  • Primero debe borrar la cookie de autenticación y la cookie de sesión pasando cookies vacías en la respuesta al cierre de sesión.

    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 dio el ejemplo anterior de cómo evitar el almacenamiento en caché después de cerrar sesión:

  • Es necesario para invalidar la caché en el cliente a través de la respuesta .

        // 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
fuente
1
Perdió todo el día en el trabajo para resolver este problema. Una vez conectado, el botón de cerrar sesión comenzó a llamar a una acción incorrecta en el controlador (Iniciar sesión, no cerrar sesión). Gracias, esto resolvió el problema. Entorno de desarrollo: ASP.NET 4.51 MVC 5.1
Ako
1
¡Buena respuesta! Humilde sugerencia: Utilice la forma de cookies de sesión de compensación x64igor utilizado: SessionStateSection sessionStateSection = (SessionStateSection)WebConfigurationManager.GetSection("system.web/sessionState"); HttpCookie sessionCookie = new HttpCookie(sessionStateSection.CookieName, "");. En general, el nombre de la cookie de sesión no lo es "ASP.NET_SessionId".
seebiscuit
20

Me parece que no tiene su sección de autorización web.config configurada correctamente dentro. Vea a continuación un ejemplo.

<authentication mode="Forms">
  <forms name="MyCookie" loginUrl="Login.aspx" protection="All" timeout="90" slidingExpiration="true"></forms>
</authentication>
<authorization>
  <deny users="?" />
</authorization>
jwalkerjr
fuente
Esta es una solución mucho más simple, marcaría esto como una respuesta. Como obtuve una versión de código que funciona en diferentes servidores en uno, no necesité establecer propiedades adicionales que agregó aquí y en otro lo hice. Por lo tanto, modificar el código no debería ser la solución correcta, modificar la configuración es mejor.
Vladimir Bozic
De forma predeterminada, el slideExpiration se establece en verdadero ( msdn.microsoft.com/library/1d3t3c61(v=vs.100).aspx ). Y esto finalmente dará como resultado que la cookie se vuelva inválida después de x minutos como se estableció en el tiempo de espera, y no cuando el usuario cierre sesión a través de SignOut (). Por lo tanto, esto no dará como resultado el comportamiento deseado para cerrar la sesión de un usuario utilizando FormsAuthentication. Por favor, corríjame si estoy equivocado.
OlafW
12

La clave aquí es que diga "Si escribo una URL directamente ...".

De forma predeterminada, en la autenticación de formularios, el navegador almacena en caché las páginas para el usuario. Por lo tanto, al seleccionar una URL directamente del menú desplegable del cuadro de dirección del navegador, o al escribirla, PUEDE obtener la página del caché del navegador y nunca volver al servidor para verificar la autenticación / autorización. La solución a esto es evitar el almacenamiento en caché del lado del cliente en el evento Page_Load de cada página, o en OnLoad () de su página base:

Response.Cache.SetCacheability(HttpCacheability.NoCache);

También te gustaría llamar:

Response.Cache.SetNoStore();
Phil Haselden
fuente
11

He luchado con esto antes también.

Aquí hay una analogía de lo que parece estar sucediendo ... Un nuevo visitante, Joe, ingresa al sitio e inicia sesión a través de la página de inicio de sesión utilizando FormsAuthentication. ASP.NET genera una nueva identidad para Joe y le da una cookie. Esa galleta es como la llave de la casa, y mientras Joe regrese con esa llave, puede abrir la cerradura. Cada visitante recibe una nueva clave y una nueva cerradura para usar.

Cuando FormsAuthentication.SignOut()se llama, el sistema le dice a Joe que pierda la llave. Normalmente, esto funciona, ya que Joe ya no tiene la llave, no puede entrar.

Sin embargo, si alguna vez vuelve a Joe, y lo hace tener esa llave perdida, que es dejar atrás en!

Por lo que puedo decir, ¡no hay forma de decirle a ASP.NET que cambie la cerradura de la puerta!

La forma en que puedo vivir con esto es recordar el nombre de Joe en una variable de sesión. Cuando cierra la sesión, abandono la sesión para no tener su nombre nunca más. Más tarde, para verificar si está permitido, simplemente comparo su Identity.Name con lo que tiene la sesión actual, y si no coinciden, no es un visitante válido.

En resumen, para un sitio web, ¡NO confíe User.Identity.IsAuthenticatedsin verificar también sus variables de sesión!

Glen Little
fuente
8
+ 1, creo que esto se llama 'ataque de repetición de cookies'. Hay un artículo sobre las limitaciones de FormsAuthentication.SignOut: support.microsoft.com/kb/900111
Dmitry
3
Para cualquiera que quiera seguir el enlace de arriba, está muerto. Puede intentar usar WaybackMachine para obtener una copia de esta página aquí, pero INMEDIATAMENTE intenta redirigir al usuario. web.archive.org/web/20171128133421/https://…
killa-byte
7

Después de mucha búsqueda, finalmente esto funcionó para mí. Espero que ayude.

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
fuente
Llevo años desarrollando aplicaciones web en PHP. Así que soy nuevo en MVC ... Admito que me encanta, PERO ¿quién hubiera pensado que algo tan simple como cerrar la sesión de alguien sería tan difícil? Intenté con cualquier otro script en esta página en el futuro y este es el único que funcionó. ¡Gracias por publicar!
Anthony Griggs
6

Esto funciona para mi

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
fuente
3

Parece que el código que publicó debe eliminar correctamente el token de autenticación de formularios, por lo que es posible que las carpetas / páginas en cuestión no estén realmente protegidas.

¿Ha confirmado que no se puede acceder a las páginas antes de que haya ocurrido un inicio de sesión?

¿Puede publicar la configuración web.config y el código de inicio de sesión que está utilizando?

Abram Simon
fuente
3

He estado escribiendo una clase base para todas mis páginas y llegué al mismo problema. Tenía un código como el siguiente y no funcionó. Al rastrear, el control pasa de la instrucción RedirectToLoginPage () a la siguiente línea sin ser redirigido.

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

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

Descubrí que hay dos soluciones. Para modificar FormsAuthentication.RedirectToLoginPage (); ser - estar

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

O para modificar web.config agregando

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

En el segundo caso, durante el rastreo, el control no llegó a la página solicitada. Se ha redirigido inmediatamente a la URL de inicio de sesión antes de llegar al punto de interrupción. Por lo tanto, el método SignOut () no es el problema, el método de redireccionamiento es el indicado.

Espero que pueda ayudar a alguien

Saludos

Wahid Shalaly
fuente
2
También puede llamar a Response.End () justo después de llamar a FormsAuthentication.RedirectToLoginPage ()
murki
Creo que hay un poco de falta de comunicación por parte de MS. Debe bloquear a las personas si desea que vuelvan a la página de inicio de sesión. De lo contrario, el marco le permitirá alegremente el acceso. Entonces tienes que decir la solución # 2 en esta publicación.
Josh Robinson el
3

Acabo de probar algunas de las sugerencias aquí y, aunque pude usar el botón de retroceso del navegador, cuando hice clic en una selección de menú, el token [Autorizar] para ese [ActionResult] me envió de vuelta a la pantalla de inicio de sesión.

Aquí está mi código de cierre de sesión:

        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);
        }

Aunque la función de retroceso en el navegador me devolvió y mostró el menú seguro (todavía estoy trabajando en eso) no pude hacer nada que estuviera seguro en la aplicación.

Espero que esto ayude

DonH
fuente
Gracias. Esta es la solución que funcionó para mí (no es necesario <deny users="?" />en web.config)
Alexei
3

He intentado la mayoría de las respuestas en este hilo, sin suerte. Terminé con esto:

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();
}

Lo encontré aquí: http://forums.asp.net/t/1306526.aspx/1

stoffen
fuente
3

Esta respuesta es técnicamente idéntica a Khosro.Pakmanesh. Lo estoy publicando para aclarar cómo su respuesta difiere de otras respuestas en este hilo, y en cuyo caso de uso se puede usar.

En general, para borrar una sesión de usuario, haciendo

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

efectivamente cerrará la sesión del usuario. Sin embargo , si en la misma Solicitud necesita verificar Request.isAuthenticated(como puede suceder a menudo en un Filtro de autorización, por ejemplo), encontrará que

Request.isAuthenticated == true

incluso después de que lo hiciste HttpContext.Session.Abandon()y FormsAuthentication.SignOut().

Lo único que funcionó fue hacer

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

Eso establece efectivamente Request.isAuthenticated = false.

seebiscuit
fuente
2

Esto comenzó a sucederme cuando configuré la propiedad de autenticación> formularios> Ruta en Web.config. Al eliminar eso se solucionó el problema, y ​​una FormsAuthentication.SignOut();vez más se eliminó la cookie.

BPM
fuente
1

Puede ser que inicie sesión desde un subdominio (sub1.domain.com) y luego intente cerrar sesión desde un subdominio diferente (www.domain.com).

jorsh1
fuente
1

Acabo de tener el mismo problema, donde SignOut () aparentemente no pudo eliminar correctamente el ticket. Pero solo en un caso específico, donde alguna otra lógica causó una redirección. Después de eliminar esta segunda redirección (la reemplacé con un mensaje de error), el problema desapareció.

El problema debe haber sido que la página se redirigió en el momento incorrecto, por lo tanto, no desencadenó la autenticación.

Peder Skou
fuente
1

Tengo un problema similar ahora y creo que el problema en mi caso, así como en el póster original, se debe a la redirección. Por defecto, Response.Redirect provoca una excepción que aparece inmediatamente hasta que se detecta y la redirección se ejecuta de inmediato, supongo que esto impide que la colección de cookies modificada se transmita al cliente. Si modifica su código para usar:

Response.Redirect("url", false);

Esto evita la excepción y parece permitir que la cookie se envíe correctamente al cliente.

lostatredrock
fuente
1

Simplemente intente enviar una variable de sesión cuando presione iniciar sesión. Y en la página de bienvenida, primero verifique si esa sesión está vacía de esta manera en la carga de la página o en el Evento Init:

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

Para mí, el siguiente enfoque funciona. Creo que si hay algún error después de la declaración "FormsAuthentication.SignOut ()", SingOut no funciona.

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

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

¿Estás probando / viendo este comportamiento usando IE? Es posible que IE esté sirviendo esas páginas del caché. Es notoriamente difícil hacer que IE vacíe su caché, por lo que en muchas ocasiones, incluso después de cerrar sesión, escribir la URL de una de las páginas "seguras" mostrará el contenido almacenado en caché de antes.

(He visto este comportamiento incluso cuando inicia sesión como un usuario diferente, e IE muestra la barra de "Bienvenida" en la parte superior de su página, con el nombre de usuario del usuario anterior. Hoy en día, generalmente una recarga lo actualizará, pero si es persistente , aún podría ser un problema de almacenamiento en caché).

Stobor
fuente
0

Hacer Session.abandon () y destruir la cookie funciona bastante bien. Estoy usando mvc3 y parece que el problema ocurre si vas a una página protegida, cierras sesión y accedes a través del historial de tu navegador. No es un gran problema, pero sigue siendo un poco molesto.

Sin embargo, intentar acceder a los enlaces de mi aplicación web funciona de la manera correcta.

Configurarlo para que no haga el almacenamiento en caché del navegador puede ser el camino a seguir.

James
fuente
0

Para MVC esto funciona para mí:

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

Quería agregar información para ayudar a entender el problema. La autenticación de formularios permite almacenar datos de usuario en una cookie o en la cadena de consulta de la URL. El método que admite su sitio se puede configurar en el archivo web.config.

De acuerdo con Microsoft :

El método SignOut elimina la información del ticket de autenticación de formularios de la cookie o la URL si CookiesSupported es falso .

Al mismo tiempo, dicen :

Uno de los valores de HttpCookieMode que indica si la aplicación está configurada para la autenticación de formularios sin cookies. El valor predeterminado es UseDeviceProfile .

Por último, con respecto a UseDeviceProfile, dicen :

Si la propiedad CookieMode se establece en UseDeviceProfile, la propiedad CookiesSupported se volverá verdadera si el Navegador para la Solicitud actual admite cookies y redirecciona con cookies ; de lo contrario, la propiedad CookiesSupported devolverá false.

Al unir todo esto, dependiendo del navegador del usuario, la configuración predeterminada puede hacer que CookiesSupported sea verdadera , lo que significa que el método SignOut no borra el ticket de la cookie. Esto parece contrario a la intuición y no sé por qué funciona de esta manera: esperaría que SignOut cierre la sesión del usuario bajo ninguna circunstancia.

Una forma de hacer que SignOut funcione por sí solo es cambiar el modo de cookie a "UseCookies" (es decir, se requieren cookies) en el archivo web.config:

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

Según mis pruebas, hacer esto hace que SignOut funcione solo a costa de que su sitio ahora requiera que las cookies funcionen correctamente.

RogerMKE
fuente
Creo que estás leyendo eso mal. Con respecto a SignOut (), estoy bastante seguro de que lo que significan es que se eliminará de la URL si CookiesSupported es falso, de lo contrario, de la cookie. Es decir, deberían haber escrito "El método SignOut elimina la información del ticket de autenticación de formularios de la cookie o, si CookiesSupported es falso, de la URL".
Oskar Berggren
-1

Tenga en cuenta que WIF se niega a decirle al navegador que limpie las cookies si el mensaje wsignoutcleanup de STS no coincide con la url con el nombre de la aplicación de IIS, y me refiero a CASO SENSIBLE . WIF responde con la verificación verde OK, pero no enviará el comando para eliminar las cookies al navegador.

Por lo tanto, debe prestar atención a la sensibilidad a mayúsculas y minúsculas de sus URL.

Por ejemplo, ThinkTecture Identity Server guarda las URL de los RP visitantes en una cookie, pero las hace minúsculas. WIF recibirá el mensaje wsignoutcleanup en minúsculas y lo comparará con el nombre de la aplicación en IIS. Si no coincide, no elimina las cookies, pero informará OK al navegador. Entonces, para este Identity Server, necesitaba escribir todas las URL en web.config y todos los nombres de las aplicaciones en IIS en minúsculas, para evitar tales problemas.

Además, no olvide permitir las cookies de terceros en el navegador si tiene aplicaciones fuera del subdominio de STS; de lo contrario, el navegador no eliminará las cookies incluso si WIF se lo indica.

Stefan
fuente
1
WIF? STS? ThinkTecture Identity Server? ¿Qué son todas estas cosas y cómo se relacionan con esta pregunta?
Oskar Berggren