Generating a new ASP.NET session in the current HTTPContext Generating a new ASP.NET session in the current HTTPContext asp.net asp.net

Generating a new ASP.NET session in the current HTTPContext


I would like to share my magic. Actually, no, its not yet magical.. We ought to test and evolve the code more.I only tested these code in with-cookie, InProc session mode. Put these method inside your page, and call it where you need the ID to be regenerated (please set your web app to 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);}

I have been digging around .NET Source code (that were available in http://referencesource.microsoft.com/netframework.aspx), and discovered that there is no way I could regenerate SessionID without hacking the internals of session management mechanism. So I do just that - hack SessionStateModule internal fields, so it will save the current Session into a new ID. Maybe the current HttpSessionState object still has the previous Id, but AFAIK the SessionStateModule ignored it. It just use the internal _rqId field when it has to save the state somewhere. I have tried other means, like copying SessionStateModule into a new class with a regenerate ID functionality, (I was planning to replace SessionStateModule with this class), but failed because it currently has references to other internal classes (like InProcSessionStateStore). The downside of hacking using reflection is we need to set our application to 'Full Trust'.

Oh, and if you really need the VB version, try these :

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


If you're security concious and would like the C# version of this answer removing the old field, please use the following.

private static void RegenerateSessionId(){    // Initialise variables for regenerating the session id    HttpContext Context = HttpContext.Current;    SessionIDManager manager = new SessionIDManager();    string oldId = manager.GetSessionID(Context);    string newId = manager.CreateSessionID(Context);    bool isAdd = false, isRedir = false;    // Save a new session ID    manager.SaveSessionID(Context, newId, out isRedir, out isAdd);    // Get the fields using the below and create variables for storage    HttpApplication ctx = HttpContext.Current.ApplicationInstance;    HttpModuleCollection mods = ctx.Modules;    SessionStateModule ssm = (SessionStateModule)mods.Get("Session");    FieldInfo[] fields = ssm.GetType().GetFields(BindingFlags.NonPublic | BindingFlags.Instance);    SessionStateStoreProviderBase store = null;    FieldInfo rqIdField = null, rqLockIdField = null, rqStateNotFoundField = null;    SessionStateStoreData rqItem = null;    // Assign to each variable the appropriate field values    foreach (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;        if (field.Name.Equals("_rqItem")) rqItem = (SessionStateStoreData)field.GetValue(ssm);    }    // Remove the previous session value    object lockId = rqLockIdField.GetValue(ssm);    if ((lockId != null) && (oldId != null))        store.RemoveItem(Context, oldId, lockId, rqItem);    rqStateNotFoundField.SetValue(ssm, true);    rqIdField.SetValue(ssm, newId);}


As Can Gencer mentioned - ReleaseItemExclusive doesn't remove old session from the store and that leads to that session eventually expiring and calling Session_End in Global.asax. This caused us a huge problem in production, because we are clearing Thread identity in Session_End, and because of this - users were spontaneously losing authentication on thread.

So below is the corrected code that works.

Dim oHTTPContext As HttpContext = HttpContext.CurrentDim oSessionIdManager As New SessionIDManagerDim sOldId As String = oSessionIdManager.GetSessionID(oHTTPContext)Dim sNewId As String = oSessionIdManager.CreateSessionID(oHTTPContext)Dim bIsRedir As Boolean = FalseDim bIsAdd As Boolean = FalseoSessionIdManager.SaveSessionID(oHTTPContext, sNewId, bIsRedir, bIsAdd)Dim oAppContext As HttpApplication = HttpContext.Current.ApplicationInstanceDim oModules As HttpModuleCollection = oAppContext.ModulesDim oSessionStateModule As SessionStateModule = _  DirectCast(oModules.Get("Session"), SessionStateModule)Dim oFields() As FieldInfo = _  oSessionStateModule.GetType.GetFields(BindingFlags.NonPublic Or _                                        BindingFlags.Instance)Dim oStore As SessionStateStoreProviderBase = NothingDim oRqIdField As FieldInfo = NothingDim oRqItem As SessionStateStoreData = NothingDim oRqLockIdField As FieldInfo = NothingDim oRqStateNotFoundField As FieldInfo = NothingFor 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 IfNextIf 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