2009-09-02 42 views
24

Como resultado de una prueba de penetración en contra de algunos de nuestros productos en la tubería, lo que parecía ser en ese momento un problema "fácil" de arreglar está resultando ser una Duro.Generando una nueva sesión de ASP.NET en el HTTPContext actual

Claro que no debería ser, por supuesto, ¿por qué simplemente generar una nueva sesión para el actual HTTPContext ser tan difícil? ¡Extraño! Todos modos- He escrito un poco de clase descarada utilidad para "just do it":

(disculpas para el formato de código/relieve/Visual Basic I debo estar haciendo algo mal)


Imports System.Web 
Imports System.Web.SessionState 

Public Class SwitchSession 

    Public Shared Sub SetNewSession(ByVal context As HttpContext) 
     ' This value will hold the ID managers action to creating a response cookie 
     Dim cookieAdded As Boolean 
     ' We use the current session state as a template 
     Dim state As HttpSessionState = context.Session 
     ' We use the default ID manager to generate a new session id 
     Dim idManager As New SessionIDManager() 
     ' We also start with a new, fresh blank state item collection 
     Dim items As New SessionStateItemCollection() 
     ' Static objects are extracted from the current session context 
     Dim staticObjects As HttpStaticObjectsCollection = _ 
      SessionStateUtility.GetSessionStaticObjects(context) 
     ' We construct the replacement session for the current, some parameters are new, others are taken from previous session 
     Dim replacement As New HttpSessionStateContainer(_ 
       idManager.CreateSessionID(context), _ 
       items, _ 
       staticObjects, _ 
       state.Timeout, _ 
       True, _ 
       state.CookieMode, _ 
       state.Mode, _ 
       state.IsReadOnly) 
     ' Finally we strip the current session state from the current context 
     SessionStateUtility.RemoveHttpSessionStateFromContext(context) 
     ' Then we replace the assign the active session state using the replacement we just constructed 
     SessionStateUtility.AddHttpSessionStateToContext(context, replacement) 
     ' Make sure we clean out the responses of any other inteferring cookies 
     idManager.RemoveSessionID(context) 
     ' Save our new cookie session identifier to the response 
     idManager.SaveSessionID(context, replacement.SessionID, False, cookieAdded) 
    End Sub 

End Class 

Funciona está bien para el resto de la solicitud, y se identifica correctamente como la nueva sesión (por ejemplo, HTTPContext.Current.Session.SessionID devuelve el identificador de sesión recién generado).

Sorpresa sorpresa entonces, que cuando la próxima petición acierta en el servidor, el HTTPContext.Session (un objeto HTTPSessionState) se identifica con el correcto SessionID, pero tiene IsNewSession conjunto de True y esté vacía, perdiendo todos los valores de sesión definido en el solicitud previa

Debe haber algo especial sobre el objeto HTTPSessionState anterior que se elimina de la solicitud inicial, un controlador de eventos aquí, una devolución de llamada allí, algo que maneja la persistencia de los datos de la sesión entre solicitudes o simplemente algo que me falta.

¿Alguien tiene magia para compartir?

+1

Desarrollé mi clase 'SwitchSession' dándole un poco de estado (la sesión de 'reemplazo') y cableando eventos' SessionStateModule' para la instancia de la aplicación ASP.NET activa. Cuando el evento 'Start' se activa, comprueba si la sesión generada de ASP.NET tiene el mismo' SessionID' y copia todos los valores de estado de la sesión de la solicitud anterior. Obviamente, solo funciona si todas las solicitudes provienen de la instancia 'HTTPApplication' que manejó la solicitud anterior. Estoy usando un reflector para profundizar un poco más en el 'SessionStateModule', pero no es bonito. Por favor vote esta pregunta! – Rabid

+0

Llegué al mismo lugar que usted (llegó a su página haciendo una búsqueda de RemoveHttpSessionStateFromContext). Lamentablemente, también golpeó la misma pared que usted: parece que no se puede generar una nueva sesión. La clave es, por supuesto, SessionStateModule.CompleteAcquiredState(), que es extremadamente difícil de conseguir, el enfoque de reflexión de Yudhi sería una forma de llegar a él, pero no estoy seguro de que valga la pena. Debo decir que, por mucho que me guste C#, la API de .NET ha sido una gran decepción, ¡cómo no pueden exponer esto! – marq

+0

FYI: CompleteAcquiredState() llama a SessionStateUtility.AddDelayedHttpSessionStateToContext(), que hace toda la magia de una nueva sesión. – marq

Respuesta

32

Me gustaría compartir mi magia. En realidad, no, aún no es mágico. Deberíamos probar y evolucionar más el código. Solo probé estos códigos en-cookie, modo de sesión InProc. Ponga estos métodos dentro de su página, y llamarlo donde se necesita la identificación para ser regenerado (por favor, defina su aplicación web a Plena confianza):

void regenerateId() 
{ 
    System.Web.SessionState.SessionIDManager manager = new System.Web.SessionState.SessionIDManager(); 
    string oldId = manager.GetSessionID(Context); 
    string newId = manager.CreateSessionID(Context); 
    bool isAdd = false, isRedir = false; 
    manager.SaveSessionID(Context, newId, out isRedir, out isAdd); 
    HttpApplication ctx = (HttpApplication)HttpContext.Current.ApplicationInstance; 
    HttpModuleCollection mods = ctx.Modules; 
    System.Web.SessionState.SessionStateModule ssm = (SessionStateModule)mods.Get("Session"); 
    System.Reflection.FieldInfo[] fields = ssm.GetType().GetFields(BindingFlags.NonPublic | BindingFlags.Instance); 
    SessionStateStoreProviderBase store = null; 
    System.Reflection.FieldInfo rqIdField = null, rqLockIdField = null, rqStateNotFoundField = null; 
    foreach (System.Reflection.FieldInfo field in fields) 
    { 
     if (field.Name.Equals("_store")) store = (SessionStateStoreProviderBase)field.GetValue(ssm); 
     if (field.Name.Equals("_rqId")) rqIdField = field; 
     if (field.Name.Equals("_rqLockId")) rqLockIdField = field; 
     if (field.Name.Equals("_rqSessionStateNotFound")) rqStateNotFoundField = field; 
    } 
    object lockId = rqLockIdField.GetValue(ssm); 
    if ((lockId != null) && (oldId !=null)) store.ReleaseItemExclusive(Context, oldId, lockId); 
    rqStateNotFoundField.SetValue(ssm, true); 
    rqIdField.SetValue(ssm, newId); 
} 

he estado cavando alrededor de código .NET Fuente (que estaban disponibles en el http://referencesource.microsoft.com/netframework.aspx), y descubrió que no hay forma de que pueda regenerar SessionID sin hackear las funciones internas del mecanismo de administración de sesión. Así que hago sólo eso - hackear campos internos SessionStateModule, por lo que guardará la sesión actual en una nueva identificación. Tal vez el objeto HttpSessionState actual todavía tiene el identificador anterior, pero que yo sepa el SessionStateModule ignoró. Solo usa el campo _rqId interno cuando tiene que guardar el estado en alguna parte. He probado otros medios, como copiar SessionStateModule en una nueva clase con una funcionalidad Identificación regenerado, (que estaba planeando para reemplazar SessionStateModule con esta clase), pero fracasó debido a que actualmente tiene referencias a otras clases internas (como InProcSessionStateStore).La desventaja de la piratería mediante el uso de la reflexión es que debemos establecer nuestra aplicación en 'Confianza total'.

Ah, y si realmente se necesita la versión VB, probar estos:

Sub RegenerateID() 
    Dim manager 
    Dim oldId As String 
    Dim newId As String 
    Dim isRedir As Boolean 
    Dim isAdd As Boolean 
    Dim ctx As HttpApplication 
    Dim mods As HttpModuleCollection 
    Dim ssm As System.Web.SessionState.SessionStateModule 
    Dim fields() As System.Reflection.FieldInfo 
    Dim rqIdField As System.Reflection.FieldInfo 
    Dim rqLockIdField As System.Reflection.FieldInfo 
    Dim rqStateNotFoundField As System.Reflection.FieldInfo 
    Dim store As SessionStateStoreProviderBase 
    Dim field As System.Reflection.FieldInfo 
    Dim lockId 
    manager = New System.Web.SessionState.SessionIDManager 
    oldId = manager.GetSessionID(Context) 
    newId = manager.CreateSessionID(Context) 
    manager.SaveSessionID(Context, newId, isRedir, isAdd) 
    ctx = HttpContext.Current.ApplicationInstance 
    mods = ctx.Modules 
    ssm = CType(mods.Get("Session"), System.Web.SessionState.SessionStateModule) 
    fields = ssm.GetType.GetFields(System.Reflection.BindingFlags.NonPublic Or System.Reflection.BindingFlags.Instance) 
    store = Nothing : rqLockIdField = Nothing : rqIdField = Nothing : rqStateNotFoundField = Nothing 
    For Each field In fields 
     If (field.Name.Equals("_store")) Then store = CType(field.GetValue(ssm), SessionStateStoreProviderBase) 
     If (field.Name.Equals("_rqId")) Then rqIdField = field 
     If (field.Name.Equals("_rqLockId")) Then rqLockIdField = field 
     If (field.Name.Equals("_rqSessionStateNotFound")) Then rqStateNotFoundField = field 
    Next 
    lockId = rqLockIdField.GetValue(ssm) 
    If ((Not IsNothing(lockId)) And (Not IsNothing(oldId))) Then store.ReleaseItemExclusive(Context, oldId, lockId) 
    rqStateNotFoundField.SetValue(ssm, True) 
    rqIdField.SetValue(ssm, newId) 

End Sub 
+1

Gracias por su contribución, mi conclusión fue similar, que el comportamiento nativo del módulo de estado de sesión requeriría manipulación. La elevación a permisos de confianza total es una pena, especialmente porque fue para solucionar un problema de seguridad (el problema e iirc es la fijación de la sesión). Buen trabajo :) – Rabid

+0

Funciona muy bien. Session.SessionID conserva el valor anterior para el resto de la solicitud. ¿Hay alguna actualización? –

+0

+1 y muchas gracias por este código! Tuve un problema al generar una solicitud secundaria utilizando HttpRuntime.ProcessRequest() en una petición de trabajo simple creada a partir del contexto actual; las solicitudes comenzaron a comportarse mal si una solicitud anterior había escrito en la sesión. El uso de su código para cambiar la ID durante el código de ejecución de la solicitud del niño resolvió ese problema. – mawtex

0

¿Ha considerado utilizar el HttpSessionState.Abandon method? Eso debería limpiar todo. A continuación, iniciar una nueva sesión y rellenarlo con todos los elementos que la memoria a partir de su código de seguridad.

Session.Abandon(); debería ser suficiente. De lo contrario podría tratar de hacer un esfuerzo adicional con un par de llamadas más si todavía está siendo terca:

Session.Contents.Abandon(); 
Session.Contents.RemoveAll(); 
+3

Desafortunadamente, este enfoque parece reutilizar el mismo identificador de sesión. Necesito que se genere un nuevo identificador de sesión :( – Rabid

0

¿Puede usted no acaba de establecer:

<sessionState regenerateExpiredSessionId="False" /> 

en web.config, y luego usar la solución sugerido por Ahmad?

+3

Desafortunadamente, aún conserva el 'SessionID' activo, que se usa cuando se produce la próxima solicitud. Sin embargo, ¿cuál es el significado de 'caducado' en este contexto? expiró la sesión, no puedo ver una manera de 'expirar' la sesión que no sea la caducidad de la cookie del cliente. Eso no ayuda a mi 'HTTPContext' activo. Pero aún así, usando este método, después de haber abandoné la sesión y eliminé todos los contenidos, luego autentico a mi usuario y coloco algo en la sesión, porque la sesión ha sido 'abandonada', está vacía en la siguiente solicitud .(:(:(:( – Rabid

0

Por favor MVC4 tienen este código:

System.Web.SessionState.SessionIDManager manager = new System.Web.SessionState.SessionIDManager(); 
      HttpContext Context = System.Web.HttpContext.Current; 
      string oldId = manager.GetSessionID(Context); 
      string newId = manager.CreateSessionID(Context); 
      bool isAdd = false, isRedir = false; 
      manager.SaveSessionID(Context, newId, out isRedir, out isAdd); 
      HttpApplication ctx = (HttpApplication)System.Web.HttpContext.Current.ApplicationInstance; 
      HttpModuleCollection mods = ctx.Modules; 
      System.Web.SessionState.SessionStateModule ssm = (SessionStateModule)mods.Get("Session"); 
      System.Reflection.FieldInfo[] fields = ssm.GetType().GetFields(BindingFlags.NonPublic | BindingFlags.Instance); 
      SessionStateStoreProviderBase store = null; 
      System.Reflection.FieldInfo rqIdField = null, rqLockIdField = null, rqStateNotFoundField = null; 
      foreach (System.Reflection.FieldInfo field in fields) 
      { 
       if (field.Name.Equals("_store")) store = (SessionStateStoreProviderBase)field.GetValue(ssm); 
       if (field.Name.Equals("_rqId")) rqIdField = field; 
       if (field.Name.Equals("_rqLockId")) rqLockIdField = field; 
       if (field.Name.Equals("_rqSessionStateNotFound")) rqStateNotFoundField = field; 
      } 
      object lockId = rqLockIdField.GetValue(ssm); 
      if ((lockId != null) && (oldId != null)) store.ReleaseItemExclusive(Context, oldId, lockId); 
      rqStateNotFoundField.SetValue(ssm, true); 
      rqIdField.SetValue(ssm, newId); 
+1

-1: ¿cómo es esto diferente de http://stackoverflow.com/a/4420114/76337? –

1

Como se mencionó Can Gencer - ReleaseItemExclusive no retire la vieja sesión desde la tienda y eso lleva a que esa sesión expire y llame a Session_End en Global.asax. Esto nos causó un gran problema en la producción, porque estamos borrando la identidad del subproceso en Session_End, y debido a esto, los usuarios estaban perdiendo espontáneamente la autenticación en el hilo.

A continuación se muestra el código corregido que funciona.

Dim oHTTPContext As HttpContext = HttpContext.Current 

Dim oSessionIdManager As New SessionIDManager 
Dim sOldId As String = oSessionIdManager.GetSessionID(oHTTPContext) 
Dim sNewId As String = oSessionIdManager.CreateSessionID(oHTTPContext) 

Dim bIsRedir As Boolean = False 
Dim bIsAdd As Boolean = False 
oSessionIdManager.SaveSessionID(oHTTPContext, sNewId, bIsRedir, bIsAdd) 

Dim oAppContext As HttpApplication = HttpContext.Current.ApplicationInstance 

Dim oModules As HttpModuleCollection = oAppContext.Modules 

Dim oSessionStateModule As SessionStateModule = _ 
    DirectCast(oModules.Get("Session"), SessionStateModule) 

Dim oFields() As FieldInfo = _ 
    oSessionStateModule.GetType.GetFields(BindingFlags.NonPublic Or _ 
             BindingFlags.Instance) 

Dim oStore As SessionStateStoreProviderBase = Nothing 
Dim oRqIdField As FieldInfo = Nothing 
Dim oRqItem As SessionStateStoreData = Nothing 
Dim oRqLockIdField As FieldInfo = Nothing 
Dim oRqStateNotFoundField As FieldInfo = Nothing 

For Each oField As FieldInfo In oFields 
    If (oField.Name.Equals("_store")) Then 
     oStore = DirectCast(oField.GetValue(oSessionStateModule), _ 
          SessionStateStoreProviderBase) 
    End If 
    If (oField.Name.Equals("_rqId")) Then 
     oRqIdField = oField 
    End If 
    If (oField.Name.Equals("_rqLockId")) Then 
     oRqLockIdField = oField 
    End If 
    If (oField.Name.Equals("_rqSessionStateNotFound")) Then 
     oRqStateNotFoundField = oField 
    End If 
    If (oField.Name.Equals("_rqItem")) Then 
     oRqItem = DirectCast(oField.GetValue(oSessionStateModule), _ 
          SessionStateStoreData) 
    End If 
Next 

If oStore IsNot Nothing Then 

    Dim oLockId As Object = Nothing 

    If oRqLockIdField IsNot Nothing Then 
     oLockId = oRqLockIdField.GetValue(oSessionStateModule) 
    End If 

    If (oLockId IsNot Nothing) And (Not String.IsNullOrEmpty(sOldId)) Then 
     oStore.ReleaseItemExclusive(oHTTPContext, sOldId, oLockId) 
     oStore.RemoveItem(oHTTPContext, sOldId, oLockId, oRqItem) 
    End If 

    oRqStateNotFoundField.SetValue(oSessionStateModule, True) 
    oRqIdField.SetValue(oSessionStateModule, sNewId) 

End If 
+0

Max, ¿podrías traducirlo a C#? – vkelman

Cuestiones relacionadas