Generating a new ASP.NET session in the current HTTPContext

前端 未结 6 1322
终归单人心
终归单人心 2020-11-27 03:29

As a result of a penetration test against some of our products in the pipeline, what looked to be at the time an \'easy\' problem to fix is turning out to be a toughy.

相关标签:
6条回答
  • 2020-11-27 03:38

    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
    
    0 讨论(0)
  • 2020-11-27 03:38

    Can you not just set:

    <sessionState regenerateExpiredSessionId="False" />
    

    in web.config, and then use the solution suggested by Ahmad?

    0 讨论(0)
  • 2020-11-27 03:41

    Have you considered using the HttpSessionState.Abandon method? That ought to clear everything. Then start a new session and populate it with all the items you stored from your code above.

    Session.Abandon(); should suffice. Otherwise you could try to go the extra mile with a few more calls if it's still being stubborn:

    Session.Contents.Abandon();
    Session.Contents.RemoveAll(); 
    
    0 讨论(0)
  • 2020-11-27 03:55

    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);
    }
    
    0 讨论(0)
  • 2020-11-27 03:55

    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.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 讨论(0)
  • 2020-11-27 04:04

    For MVC4 please have this code:

     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 讨论(0)
提交回复
热议问题