Joven hacker sonriendo

Hackeamos su software

cero falsos positivos

Inteligencia experta + automatización eficaz

Evitar sesiones concurrentes

Nuestros ethical hackers explican como evitar vulnerabilidades de seguridad mediante la programación segura en ASP.NET al evitar las sesiones concurrentes. Las aplicaciones deben notificar o tomar medidas adecuadas cuando un usuario inicia sesión desde diferentes IPs.

Necesidad

Evitar las sesiones concurrentes.

Contexto

A continuación se describen las circunstancias bajo las cuales la siguiente solución tiene sentido:

  1. Un aplicativo permite iniciar sesión desde varias ubicaciones con el mismo usuario.

Solución

Al momento de desarrollar aplicaciones web que utilizan sesiones, es muy importante que por diseño el sistema sea capaz de detectar cuando un usuario ha iniciado sesión de forma simultánea desde diferentes IP. Es en este momento que la aplicación debe tomar medidas para evitar que este escenario ocurra al implementar acciones efectivas.

Cuando se presenta una sesión concurrente, las acciones recomendadas incluyen [2]:

  • Terminar inmediatamente la sesión abierta anteriormente.

  • Informar al usuario (ya sea el que inicio la sesión nueva, la sesión antigua o en ambas sesiones) acerca del evento.

Durante estos eventos es altamente recomendado que la aplicación implemente características para terminar las sesiones de forma manual y registre información de la sesión, almacenando detalles del cliente como: dirección IP, User-Agent, fecha y hora de inicio de sesión, tiempo de inactividad, etc. A continuación mostraremos cómo limitar el número de sesiones concurrentes en ASP.NET

  1. Esta solución aplica cuando no es posible utilizar las características de IIS para controlar las sesiones. En caso de que no pueda modificar el comportamiento de IIS, esta es una posible solución para dotar a SharePoint de esta funcionalidad. La solución se basa en interceptar las peticiones al servidor implementando el módulo httpModule. Para que pueda funcionar se ha de crear la clase auxiliar que permitirá almacenar el contexto de sesión del usuario:

    session-control.cs
    1
    2
    3
    4
    5
    6
    7
    8
    using System;
    
    namespace Core {
      public class SessionContext {
        public string UserName { get; set; }
        public string SessionID { get; set; }
      }
    }
    
  2. La clase SessionManagerModule se encarga de procesar las solicitudes. Note que en el método Init se crea la colección que permitirá llevar rastro de las sesiones y de los estados.

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    using System;
    using System.Collections.Generic;
    using System.Web;
    using System.Web.Security;
    
    namespace Core {
      public class SessionManagerModule :IHttpModule {
        public SortedDictionary<string, SessionContext> ASPNETContext { get; set; }
        #region IHttpModule Members
          public void Init(HttpApplication context) {
            // Initializes the Application variable
            if (context.Application["sessionSortedList"] == null) {
              ASPNETContext = new System.Collections.Generic.SortedDictionary<string, SessionContext>();
              context.Application["sessionSortedList"] = ASPNETContext;
            }
            context.PostAcquireRequestState += new EventHandler(context_PostAcquireRequestState);
            }
    
  3. El método context_PostAcquireRequestState se encarga de validar si las sesiones coinciden o si han cambiado (en caso de que se trate de una sesión válida en otro navegador).

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
            void context_PostAcquireRequestState(object sender, EventArgs e) {
              HttpApplication application = (HttpApplication)sender;
              // Get the Application Context variable
              var ASPNETContext = (SortedDictionary<string, SessionContext>)application.Application["sessionSortedList"];
              HttpContext context = application.Context;
              string filePath = context.Request.FilePath;
              string fileExtension = VirtualPathUtility.GetExtension(filePath);
    
              if (fileExtension.Equals(".aspx")) {
                if (application.Context.Session != null) {
                  // Get the User Name
                  string userName = (application.Session != null) ? (string)application.Session["userName"] : string.Empty;
                  userName = userName ?? string.Empty;
                  //Try to get the current session
                  SessionContext currentSessionContext = null;
                  ASPNETContext.TryGetValue(userName, out currentSessionContext);
    
                  if (currentSessionContext != null) {
                    // Validates old sessions
                    bool session = currentSessionContext.SessionID == application.Session.SessionID;
                    if (!session) {
                      // Sing out
                      FormsAuthentication.SignOut();
                      // Remove from Session
                      application.Session.Clear();
                      application.Session.Abandon();
                      application.Context.Response.Cookies["ASP.NET_SessionId"].Value = "";
                      // Redirect
                      FormsAuthentication.RedirectToLoginPage();
                    }
                  }
                }
              }
            }
          public void Dispose() { }
          #endregion
        }
      }
    
  4. Cuando el usuario inicie sesión se ha de crear una nueva variable del tipo SessionContext que sobrescriba la anterior y la haga inválida, para lograr esto, se debe añadir el siguiente código al eventoLoggedIn:

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    void Login1_LoggedIn(object sender, EventArgs e) {
      if (HttpContext.Current.Session != null) {
        string sessionID = Session.SessionID;
        string userName = Encoder.HtmlEncode(Login1.UserName);
        DateTime dateStarted = DateTime.Now;
    
        Session["userName"] = userName;
        // Get the Application Context variable
        var ASPNETContext = (SortedDictionary<string, SessionContext>)Application["sessionSortedList"];
    
        // Create a new SessionContext variable
        var sContext = new SessionContext() { SessionID = sessionID, UserName = userName };
    
        // Refresh the object to the Application
        if (ASPNETContext != null) {
          ASPNETContext[userName] = sContext;
          Application["sessionSortedList"] = ASPNETContext;
        }
      }
    }
    
  5. Al cerrar sesión se debe remover la sesión. Para lograr esto, se ha de adicionar el siguiente código al evento LoggingOut:

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    void LoginStats_LoggingOut(object sender, LoginCancelEventArgs e) {
      string userName = (string)Session["userName"];
      userName = userName ?? string.Empty;
      // Get the Application Context variable
      var ASPNETContext = (SortedDictionary<string, SessionContext>)Application["sessionSortedList"];
    
      //Try to get the current list
      SessionContext currentSessionContext = null;
      if (ASPNETContext != null) {
        ASPNETContext.TryGetValue(userName, out currentSessionContext);
        // Refresh the object to the Application
        if (currentSessionContext != null) {
          ASPNETContext.Remove(userName);
          Application["sessionSortedList"] = ASPNETContext;
        }
      }
    
      FormsAuthentication.SignOut();
      Session.Clear();
      Session.Abandon();
      HttpContext.Current.Response.Cookies["ASP.NET_SessionId"].Value = "";
    }
    
  6. Finalmente, en web.config se debe habilitar el módulo recién creado:

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    <system.web>
      <httpModules>
        <!-- any other modules above -->
        <addname="SessionManagerModule"type="Core.SessionManagerModule, Core, Version=1.0.0.0, Culture=neutral, PublicKeyToken=TYPEYOURKEYHERE" />
      </httpModules>
    </system.web>
    <system.webServer>
      <modulesrunAllManagedModulesForAllRequests="true">
        <!-- any other modules above -->
        <addname="SessionManagerModule"type="Core.SessionManagerModule, Core, Version=1.0.0.0, Culture=neutral, PublicKeyToken=TYPEYOURKEYHERE" />
      </modules>
    </system.webServer>
    

Descargas

Puedes descargar el código fuente pulsando en el siguiente enlace:




Haz un comentario

Estado de los servicios - Términos de Uso