パイプライン内の一部の製品に対する侵入テストの結果、修正するのが「簡単な」問題であると思われていたのは、困難であることが判明しています。
もちろんそれは当然のことではありませんが、なぜ現在のHTTPContext
の新しいセッションを生成することがそれほど難しいのでしょうか?奇妙な!とにかく、私は「ただやる」ための生意気な小さなユーティリティクラスを書きました:
(コードの書式設定/強調表示/ Visual Basic Iの謝罪何か間違っていること)
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内部フィールドをハックして、現在のセッションを新しいIDに保存します。たぶん、現在のHttpSessionStateオブジェクトは以前のIDをまだ持っていますが、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
Gencerが言及したように-ReleaseItemExclusiveはストアから古いセッションを削除せず、そのセッションが最終的に期限切れになり、Global.asaxのSession_Endを呼び出すことになります。これにより、Session_EndでスレッドIDをクリアするため、およびユーザーのためにスレッドの認証が自然に失われるため、本番環境で大きな問題が発生しました。
したがって、以下は機能する修正されたコードです。
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);
}
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);
あなただけを設定することはできません:
<sessionState regenerateExpiredSessionId="False" />
web.configで、次にAhmadによって提案されたソリューションを使用しますか?
HttpSessionState.Abandon method の使用を検討しましたか?それはすべてをクリアするはずです。次に、新しいセッションを開始して、上記のコードから保存したすべてのアイテムを追加します。
Session.Abandon();
で十分です。それ以外の場合は、まだ頑固な場合は、さらにいくつかの呼び出しを使用して、さらに1マイル進むことができます。
Session.Contents.Abandon();
Session.Contents.RemoveAll();