Generar un nuevo ASP.NET sesión en el HttpContext actual


Como resultado de una prueba de penetración contra algunos de nuestros productos en la tubería, lo que parecía ser en ese momento un problema "fácil" de solucionar está resultando ser un problema difícil.

No es que debería, por supuesto, me refiero a por qué simplemente generar una nueva sesión para el actual HTTPContext ¿tan difícil? ¡Bizarro! De todos modos-He escrito una pequeña clase de utilidad descarada para "just do it":

(disculpas por formatear/resaltar el código / Visual Basic I debe {[17] } estar haciendo algo malo)


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 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 siguiente solicitud llega al servidor, el HTTPContext.Session (un objeto HTTPSessionState) se identifica con el SessionID correcto, pero tiene IsNewSession establecido en True, y está vacío, perdiendo todos los valores de sesión establecidos en la solicitud anterior.

Así que debe haber algo especial ¿sobre el objeto anterior HTTPSessionState 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 sesión en las solicitudes, o simplemente algo que me falta?

¿Alguien tiene algo de magia para compartir?

Author: Rabid, 2009-09-02

5 answers

Me gustaría compartir mi magia. En realidad, no, todavía no es mágico.. Deberíamos probar y evolucionar más el código. Solo probé este código en el modo de sesión con-cookie, InProc. Coloque estos métodos dentro de su página y llámelo donde necesite que se regenere el ID (por favor, configure su aplicación web en Full Trust):

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 investigando el código fuente de. NET (que estaban disponibles en http://referencesource.microsoft.com/netframework.aspx ), y descubrió que no hay manera podría regenerar SessionID sin hackear el funcionamiento interno del mecanismo de administración de sesiones. Así que hago solo eso - hack SessionStateModule campos internos, por lo que guardará la sesión actual en un nuevo ID. Tal vez el objeto HttpSessionState actual todavía tenga el Id anterior, pero AFAIK el SessionStateModule lo ignoró. Solo usa el campo interno _rqId cuando tiene que guardar el estado en algún lugar. He intentado otros medios, como copiar SessionStateModule en una nueva clase con un ID de regeneración funcionalidad, (estaba planeando reemplazar SessionStateModule con esta clase), pero falló porque actualmente tiene referencias a otras clases internas (como InProcSessionStateStore). La desventaja de hackear usando reflexión es que necesitamos establecer nuestra aplicación en 'Confianza total'.

Ah, y si realmente necesitas la versión VB, prueba 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
 32
Author: YudhiWidyatama,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-12-12 02:24:05

Como Can Gencer mencionó - ReleaseItemExclusive no elimina la sesión antigua de la tienda y eso conduce a que la sesión finalmente 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 hilo en Session_End, y debido a esto, los usuarios estaban perdiendo espontáneamente la autenticación en el hilo.

Así que a continuación está 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
 1
Author: Max Ostrinsky,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-03-17 11:59:52

¿Ha considerado usar el HttpSessionState?Abandonar el método ? Eso debería aclararlo todo. A continuación, inicie una nueva sesión y rellénela con todos los elementos que almacenó desde el código anterior.

Session.Abandon(); debería ser suficiente. De lo contrario, podría intentar hacer un esfuerzo adicional con unas cuantas llamadas más si todavía está siendo terco:

Session.Contents.Abandon();
Session.Contents.RemoveAll(); 
 0
Author: Ahmad Mageed,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-09-02 15:46:18

No puedes simplemente establecer:

<sessionState regenerateExpiredSessionId="False" />

En la web.config, y luego utilizar la solución sugerida por Ahmad?

 0
Author: Jason,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-09-10 13:34:12

Para MVC4 por favor tenga 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);
 0
Author: arunprakash,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-06-19 09:49:38