如何在登录ASP.NET后更改会话ID

13

我有一个使用表单验证和成员资格的网站。用户必须启用Cookie才能使用该网站。现在有人要求我更改代码,以便在用户登录后立即更改会话ID。显然,这可以防止会话固定攻击(http://en.wikipedia.org/wiki/Session_fixation)。有没有人知道如何在不丢失整个会话的情况下更改会话ID?PHP有一种特定的方法可以实现这一点,但我找不到.NET的等效方法。

5个回答

6

我曾在生成当前HTTPContext中的新ASP.NET会话类似的问题上进行过回答。基本上,我们必须更改一些SessionStateModule内部状态才能重新生成会话ID而不会丢失Session中的对象。我使用了反射来将_rqId字段设置为新ID和_rqSessionStateNotFound设置为true。缺点是我们必须授予应用程序“完全信任”权限。


2
被采纳的答案引用了一篇针对.NET 1.1的文章(它实际上并没有解决会话固定问题,只是旧会话ID的重用),但这个解决方案更加优雅,并且在我们的.NET 4.5应用程序中运行良好(一个MVC站点和一个Web Forms站点)。 - mlhDev

6
这是一篇关于此问题的博客文章:点击这里
ASP.NET不直接支持重新生成会话ID的功能。关于此问题的文档请参见这里。可以通过将ASPNET_SessionID值设置为空字符串并重定向以重新生成该值的方式来实现,但这种方法并不完美。此处提供了更多详细信息。

1
我也考虑过这个问题,但是重新生成后,所有存储在会话对象中的对象都将丢失... - MemoryLeak
5
你提供的博客文章已不再可用。 - Larry Silverman

1

这是一个我正在重新启动的非常古老的问题,但这是解决方案:

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;

我知道已经过了一段时间。但我们尝试了你的代码,它对我们不起作用。sessionId 不等于 "5vonjb4mtb1of2fxvhjvkh5d"。 - mosquito87
我已成功执行此操作。Session ID 已更改为新的 Session ID。 - pinopino
2
这让我们更接近了,但还不够接近——我们仍在努力。这确实更新了会话ID并发送了Cookie更新,就像您所期望的那样,但我们仍然希望在同一请求期间使用该会话。我们现在无法做到这一点——似乎会话包是先前的;下一个请求看到了新的会话包,但我们的值不在那里。 - mlhDev

0

微软已经废弃的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 ) );
}

0
对于那些正在搜索并看到所有这些反射攻击的人,以及在处理会话固定问题时遇到困难的人。唯一的方法是让asp.net创建一个新的会话ID,就是伪造一个返回null的SessionIDManager,用于你的登录页面。这样,asp.net就会认为浏览器从未发送过cookie。
以下是步骤:
1. 创建一个CustomSessionIDManager,注意YourLoginController/LoginMethod的细节。
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.

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接