由于对我们的一些产品进行了渗透测试,当时看起来很容易解决的“容易”问题变得艰难。
当然不应该这样,我的意思是为什么只为当前的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
正如Can Gencer所提到的 - ReleaseItemExclusive不会从商店中删除旧会话,并导致该会话最终到期并在Global.asax中调用Session_End。这导致我们在生产中出现了一个巨大的问题,因为我们正在清除Session_End中的线程标识,正因为如此 - 用户在线程上自发地丢失了身份验证。
以下是有效的更正代码。
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
如果您有安全意识并希望此答案的C#版本删除旧字段,请使用以下内容。
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);
}
你考虑过使用HttpSessionState.Abandon method吗?这应该清除一切。然后启动一个新会话,并使用上面代码中存储的所有项目填充它。
Session.Abandon();
应该足够了。否则,如果它仍然顽固,你可以尝试多打几个电话:
Session.Contents.Abandon();
Session.Contents.RemoveAll();
你能不能只设置:
<sessionState regenerateExpiredSessionId="False" />
在web.config中,然后使用Ahmad建议的解决方案?
对于MVC4,请拥有以下代码:
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);