Asp.net

在目前 HTTPContext 中生成新的 ASP.NET 會話

  • September 2, 2009

由於對我們正在開發中的一些產品進行了滲透測試,當時看起來“容易”解決的問題變成了棘手的問題。

不是說它當然應該,我的意思是為什麼只**為目前生成一個全新的會話HTTPContext**如此困難?奇怪!不管怎樣——我寫了一個厚臉皮的小實用類來“做它”:

(為程式碼格式化/突出顯示/ Visual Basic道歉,我一定做錯了什麼)


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

它適用於請求的其餘部分,並正確地將自己標識為新會話(例如HTTPContext.Current.Session.SessionID,返回新生成的會話標識符)。

令人驚訝的是,當下一個請求到達伺服器時,HTTPContext.Session(一個HTTPSessionState對象)將自己標識為正確的SessionID,但已IsNewSession設置為True,並且為空,失去了前一個請求中設置的所有會話值。

因此,從初始請求中刪除的前一個對像一定有什麼特別之處HTTPSessionState,這裡有一個事件處理程序,那裡有一個回調,處理跨請求持久化會話數據的東西,或者只是我缺少的東西?

有人有什麼魔法可以分享嗎?

我想分享我的魔法。實際上,不,它還沒有神奇。我們應該更多地測試和改程序式碼。我只在 with-cookie、InProc 會話模式下測試了這些程式碼。將這些方法放在您的頁面中,並在您需要重新生成 ID 的地方呼叫它(請將您的 Web 應用程序設置為完全信任):

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);
}

我一直在探勘 .NET 原始碼(可在<http://referencesource.microsoft.com/netframework.aspx>),並發現如果不破解會話管理機制的內部結構,我就無法重新生成 SessionID。所以我就是這麼做的——破解 SessionStateModule 內部欄位,所以它會將目前的 Session 保存到一個新的 ID 中。也許目前的 HttpSessionState 對象仍然具有以前的 Id,但是 AFAIK SessionStateModule 忽略了它。當它必須在某處保存狀態時,它只使用內部 _rqId 欄位。我嘗試了其他方法,比如將 SessionStateModule 複製到具有重新生成 ID 功能的新類中(我打算用這個類替換 SessionStateModule),但失敗了,因為它目前引用了其他內部類(如 InProcSessionStateStore)。使用反射進行黑客攻擊的缺點是我們需要將我們的應用程序設置為“完全信任”。

哦,如果你真的需要 VB 版本,試試這些:

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

引用自:https://stackoverflow.com/questions/1368403