我有一个使用表单验证和成员资格的网站。用户必须启用Cookie才能使用该网站。现在有人要求我更改代码,以便在用户登录后立即更改会话ID。显然,这可以防止会话固定攻击(http://en.wikipedia.org/wiki/Session_fixation)。有没有人知道如何在不丢失整个会话的情况下更改会话ID?PHP有一种特定的方法可以实现这一点,但我找不到.NET的等效方法。
我有一个使用表单验证和成员资格的网站。用户必须启用Cookie才能使用该网站。现在有人要求我更改代码,以便在用户登录后立即更改会话ID。显然,这可以防止会话固定攻击(http://en.wikipedia.org/wiki/Session_fixation)。有没有人知道如何在不丢失整个会话的情况下更改会话ID?PHP有一种特定的方法可以实现这一点,但我找不到.NET的等效方法。
我曾在生成当前HTTPContext中的新ASP.NET会话类似的问题上进行过回答。基本上,我们必须更改一些SessionStateModule内部状态才能重新生成会话ID而不会丢失Session中的对象。我使用了反射来将_rqId字段设置为新ID和_rqSessionStateNotFound设置为true。缺点是我们必须授予应用程序“完全信任”权限。
这是一个我正在重新启动的非常古老的问题,但这是解决方案:
var manager = new SessionIDManager();
bool redirected, isAdded;
manager.SaveSessionID(System.Web.HttpContext.Current,
"5vonjb4mtb1of2fxvhjvkh5d", out redirected, out isAdded);
// sessionId now equals "5vonjb4mtb1of2fxvhjvkh5d"
var sessionId = Session.SessionID;
微软已经废弃的KB文章899918中提到的方法即使对于.NET Framework 4.5 Web Forms应用程序也能很好地工作。以下是该文章的存档链接:https://www.betaarchive.com/wiki/index.php/Microsoft_KB_Archive/899918
ASP.NET如何以及为什么重复使用会话ID 文章编号:899918
文章最后修改日期:2006年9月8日
适用范围
Microsoft .NET Framework 1.1
介绍 本文描述了Microsoft ASP.NET会话ID的使用方式和原因。
更多信息 ASP.NET会话状态是一种技术,可让您存储服务器端用户特定数据。Web应用程序可以使用此数据来处理为其实例化会话状态的用户的请求。会话状态用户由会话ID标识。会话ID通过以下方法之一传递:
会话 ID 是发送到用户浏览器的 cookie 的一部分。会话 ID 嵌入在 URL 中。这种技术也被称为无 cookie 会话。会话 ID 是一个由 120 位随机数表示的 20 字符串。该字符串格式化以便可以包含在 URL 中,而不必进行 URL 编码。例如,该字符串可用于无 cookie 会话。传递会话 ID 的最常用方法是使用 cookie 存储会话 ID。
当用户首次打开其 Web 浏览器并转到实现 ASP.NET 会话状态的网站时,将向浏览器发送一个名为“ASP.NET_SessionId”的 cookie 和一个 20 字符值。
当用户在同一 DNS 域内浏览时,Web 浏览器继续将此 cookie 发送到其来源域。
例如,app1.tailspintoys.com 和 app2.tailspintoys.com 都是 ASP.NET 应用程序。如果用户转到 app1.tailspintoys.com,然后转到 app2.tailspintoys.com,则两个应用程序都将使用相同的 cookie 和相同的会话 ID 来跟踪用户在每个应用程序中的会话状态。应用程序不共享相同的会话状态,只共享会话 ID。
因此,您可以出于多种原因重复使用会话ID。例如,如果您重复使用会话ID,则无需执行以下操作:在收到有效的会话ID时创建新的加密唯一会话ID。 为单个域中的每个ASP.NET应用程序创建新的会话ID。 当Web应用程序需要登录并提供注销页面或选项时,我们建议在用户注销网站时清除会话状态。要清除会话状态,请调用Session.Abandon方法。 Session.Abandon方法允许您在不等待会话状态超时的情况下刷新会话状态。默认情况下,此超时是20分钟的滑动过期。每次用户请求Web站点并呈现会话ID cookie时,都会刷新此过期时间。 Abandon方法在会话状态对象中设置一个标志,指示应放弃会话状态。该标志将被检查,然后在页面请求结束时执行。因此,在调用Abandon方法后,用户可以在页面内使用会话对象。一旦页面处理完成,会话就会被删除。
当您使用进程内会话状态模式时,这些会话状态对象存储在HttpCache中。当满足以下条件时,HttpCache支持回调方法:
缓存条目被删除。
当缓存条目被删除时,会话状态管理器会注册Session_OnEnd事件处理程序以在此时调用。当会话状态管理器从缓存中移除一个会话状态对象时,HttpCache管理器将调用任何已注册的回调函数,实际上这个行为会触发Session_OnEnd事件处理程序。
当您放弃一个会话时,用户的浏览器中不会删除会话ID cookie。因此,一旦会话被放弃,任何针对同一应用程序的新请求都将使用相同的会话ID,但将具有新的会话状态实例。同时,如果用户在同一DNS域内打开另一个应用程序,在一个应用中调用Abandon方法后,用户将不会丢失其会话状态。
有时候,您可能不想重复使用会话ID。如果您确实不想重复使用会话ID,而且您理解不重复使用会话ID所带来的影响,请使用以下代码示例放弃会话并清除会话ID cookie:
Session.Abandon(); Response.Cookies.Add(new HttpCookie("ASP.NET_SessionId", "")); 这个代码示例从服务器清除会话状态并将会话状态cookie设置为空值。空值有效地从浏览器中清除了cookie。
当用户未从应用程序注销并且会话状态超时发生时,如果浏览器未关闭,则应用程序仍可能使用相同的会话状态 cookie。此行为会导致用户被重定向到登录页面,并呈现用户的会话状态 cookie。为了确保在打开登录页面(login.aspx)时使用新的会话 ID,请向客户端发送一个空 cookie。为此,请将 cookie 添加到响应集合中。然后,将响应集合发送回客户端。发送空 cookie 的最简单方法是使用 Response.Redirect 方法。因为 cookies 集合始终具有 ASP.NET_SessionId 的值,所以您不能仅测试此 cookie 是否存在,因为这会创建 Response.Redirect 循环。您可以在重定向到登录页面时设置查询字符串。
或者,如下面的代码示例所示,您可以使用不同的 cookie 来判断是否已经重定向到登录页面。为了增强安全性并确保没有人试图使用第二个 cookie 与 ASP.NET cookie 一起打开登录页面,以下代码示例使用 FormsAuthentication 类来加密和解密 cookie 数据。然后,代码示例设置了 5 秒的超时时间。
private void Page_Load(object sender, System.EventArgs e)
{
if( !IsPostBack && ( Request.Cookies["__LOGINCOOKIE__"] == null || Request.Cookies["__LOGINCOOKIE__"].Value == "" ) )
{
//At this point, we do not know if the session ID that we have is a new
//session ID or if the session ID was passed by the client.
//Update the session ID.
Session.Abandon();
Response.Cookies.Add(new HttpCookie("ASP.NET_SessionId", ""));
//To make sure that the client clears the session ID cookie, respond to the client to tell
//it that we have responded. To do this, set another cookie.
AddRedirCookie();
Response.Redirect( Request.Path );
}
//Make sure that someone is not trying to spoof.
try
{
FormsAuthenticationTicket ticket =
FormsAuthentication.Decrypt( Request.Cookies["__LOGINCOOKIE__"].Value );
if( ticket == null || ticket.Expired == true )
throw new Exception();
RemoveRedirCookie();
}
catch
{
//If someone is trying to spoof, do it again.
AddRedirCookie();
Response.Redirect( Request.Path );
}
Response.Write("Session.SessionID="+Session.SessionID+"<br/>");
Response.Write("Cookie ASP.NET_SessionId="+Request.Cookies["ASP.NET_SessionId"].Value+"<br/>");
}
private void RemoveRedirCookie()
{
Response.Cookies.Add(new HttpCookie("__LOGINCOOKIE__", ""));
}
private void AddRedirCookie()
{
FormsAuthenticationTicket ticket =
new FormsAuthenticationTicket(1,"Test",DateTime.Now,DateTime.Now.AddSeconds(5), false,"");
string encryptedText = FormsAuthentication.Encrypt( ticket );
Response.Cookies.Add( new HttpCookie( "__LOGINCOOKIE__", encryptedText ) );
}
public class CustomSessionIDManager : ISessionIDManager
{
private readonly SessionIDManager _sessionIDManager;
public CustomSessionIDManager()
{
_sessionIDManager = new SessionIDManager();
}
public string CreateSessionID(HttpContext context)
{
return _sessionIDManager.CreateSessionID(context);
}
public string GetSessionID(HttpContext context)
{
var path = context.Request.Path.ToString();
if (path.EndsWith("YourLoginController/LoginMethod"))
return null;
return _sessionIDManager.GetSessionID(context);
}
public void Initialize()
{
_sessionIDManager.Initialize();
}
public bool InitializeRequest(HttpContext context, bool suppressAutoDetectRedirect, out bool supportSessionIDReissue)
{
return _sessionIDManager.InitializeRequest(context, suppressAutoDetectRedirect, out supportSessionIDReissue);
}
public void RemoveSessionID(HttpContext context)
{
_sessionIDManager.RemoveSessionID(context);
}
public void SaveSessionID(HttpContext context, string id, out bool redirected, out bool cookieAdded)
{
_sessionIDManager.SaveSessionID(context, id, out redirected, out cookieAdded);
}
public bool Validate(string id)
{
return _sessionIDManager.Validate(id);
}
}```
- Put it on the `web.config`:
<system.web>
<sessionState sessionIDManagerType="CustomSessionIDManager" />
</system.web>
Now it should return a new session for each login.