65.9K
CodeProject 正在变化。 阅读更多。
Home

ASP.NET 中的 Session 固定漏洞

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.94/5 (23投票s)

2011年6月14日

CPOL

4分钟阅读

viewsIcon

181457

在本文中,我们将学习如何避免 ASP.NET 中的 Session 固定漏洞。

引言

ASP.NET Session 通过在用户浏览器中创建名为 ASP.NET_SessionId 的 cookie 来跟踪用户。每次请求都会检查此 cookie 的值,以确保提供的数据是特定于该用户的。在许多应用程序中,Session 变量用于跟踪登录的用户,即,如果该用户存在 Session 变量,则表示用户已登录,否则表示未登录。

背景 - 漏洞

每当任何数据保存到 Session 中时,都会在用户的浏览器中创建 ASP.NET_SessionId cookie。即使用户已注销(意味着通过调用 Session.Abandon()Session.RemoveAll()Session.Clear() 方法删除了 Session 数据),此 ASP.NET_SessionId cookie 及其值也不会从用户浏览器中删除。攻击者可以通过提供利用跨站脚本漏洞设置此预定义 cookie 的链接来利用此合法 cookie 值来劫持用户会话。当用户单击此链接并登录时,用户将拥有攻击者已知的相同 ASP.NET_SessionId cookie 值,并且他/她也将能够浏览用户帐户并访问与该用户相关的所有信息。这种攻击称为 Session 固定漏洞。

您可以在我的博客中找到数百个此类技巧和窍门: .NET 如何技巧和窍门

让我们创建一个演示应用程序,显示即使用户已注销并且所有 Session 数据都已删除,ASP.NET_SessionId cookie 仍然存在。

ASPX 页面
<fieldset>
    <legend>Login</legend>
    <p>Username : <asp:TextBox ID="txtU" runat="server" /> </p>
    <p>Password : <asp:TextBox ID="txtP" runat="server" /> </p>
    <p><asp:Button ID="btnSubmit" runat="server" 
            Text="Login" OnClick="LoginMe" />
    <asp:Label ID="lblMessage" runat="server" EnableViewState="false" />
    <asp:Button ID="btnLogout" runat="server" 
            Text="Logout" OnClick="LogoutMe" Visible="false" />
    </p>
</fieldset>

在上面的代码片段中,我们有两个 TextBox 控件,两个 Button 控件和一个 Label 控件。

后台代码
protected void Page_Load(object sender, EventArgs e)
{
    if (Session["LoggedIn"] != null)
    {
        lblMessage.Text = "Congratulations !, you are logged in.";
        lblMessage.ForeColor = System.Drawing.Color.Green;
        btnLogout.Visible = true;
    }
    else
    {
        lblMessage.Text = "You are not logged in.";
        lblMessage.ForeColor = System.Drawing.Color.Red;
    }
}

protected void LoginMe(object sender, EventArgs e)
{
    // Check for Username and password (hard coded for this demo)
    if (txtU.Text.Trim().Equals("u") && txtP.Text.Trim().Equals("p"))
    {
        Session["LoggedIn"] = txtU.Text.Trim();
    }
    else
    {
        lblMessage.Text = "Wrong username or password";
    }
}

protected void LogoutMe(object sender, EventArgs e)
{
    Session.Clear();
    Session.Abandon();
    Session.RemoveAll();
}

单击 Login 按钮后,在验证 TextBox 的值后,LoginMe 方法会触发并创建 Session[“LoggedIn”]

单击 Logout 按钮后,我们调用 Session.Clear()Session.Abandon()Session.RemoveAll() 方法,以确保会话变量已被删除。

输出

用户登录时的 ASP.NET_SessionId cookie

请注意下面的图片,当用户登录时,已创建 ASP.NET_SessionId cookie。

Itfunda_1896_1.JPG

单击 Login 后,返回并刷新页面。

现在,当我们单击 Logout 按钮时,即使 Session 已被放弃/删除,ASP.NET_SessionId cookie 仍然存在。

即使用户已注销,ASP.NET_SessionId cookie 仍然存在

Itfunda_4103_2.JPG

单击 Login 后,返回并刷新页面。

如何修复此漏洞

简单的修复

为避免 Session 固定漏洞攻击,我们可以在 Logout 方法中显式删除 ASP.NET_SessionId cookie。

万无一失的修复

为了彻底防范此攻击,我们可以创建另一个 cookie(例如 AuthCookie),其中包含一个唯一值,并且相同的值也可以存储在 Session 中。在每次页面加载时,我们可以将此 cookie 值与 Session 值进行匹配;如果两者都匹配,则允许用户进入应用程序,否则重定向到登录页面。

在 Logout 函数中,请确保您也删除了此新 Cookie “AuthCookie”。要删除此 cookie,只需将其过期日期/时间设置为比当前日期/时间早几个月。

因此,我修改后的此页面的代码隐藏如下所示:

修改后的代码隐藏
protected void Page_Load(object sender, EventArgs e)
{
    //NOTE: Keep this Session and Auth Cookie check
    //condition in your Master Page Page_Load event
    if (Session["LoggedIn"] != null && Session["AuthToken"] != null 
           && Request.Cookies["AuthToken"] != null)
    {
        if (!Session["AuthToken"].ToString().Equals(
                   Request.Cookies["AuthToken"].Value))
        {
            // redirect to the login page in real application
            lblMessage.Text = "You are not logged in.";
        }
        else
        {
            lblMessage.Text = "Congratulations !, you are logged in.";
            lblMessage.ForeColor = System.Drawing.Color.Green;
            btnLogout.Visible = true;
        }
    }
    else
    {
        lblMessage.Text = "You are not logged in.";
        lblMessage.ForeColor = System.Drawing.Color.Red;
    }
}

protected void LoginMe(object sender, EventArgs e)
{
    // Check for Username and password (hard coded for this demo)
    if (txtU.Text.Trim().Equals("u") && 
                  txtP.Text.Trim().Equals("p"))
    {
        Session["LoggedIn"] = txtU.Text.Trim();
        // createa a new GUID and save into the session
        string guid = Guid.NewGuid().ToString();
        Session["AuthToken"] = guid;
        // now create a new cookie with this guid value
        Response.Cookies.Add(new HttpCookie("AuthToken", guid));

    }
    else
    {
        lblMessage.Text = "Wrong username or password";
    }
}

protected void LogoutMe(object sender, EventArgs e)
{
    Session.Clear();
    Session.Abandon();
    Session.RemoveAll();

    if (Request.Cookies["ASP.NET_SessionId"] != null)
    {
        Response.Cookies["ASP.NET_SessionId"].Value = string.Empty;
        Response.Cookies["ASP.NET_SessionId"].Expires = DateTime.Now.AddMonths(-20);
    }

    if (Request.Cookies["AuthToken"] != null)
    {
        Response.Cookies["AuthToken"].Value = string.Empty;
        Response.Cookies["AuthToken"].Expires = DateTime.Now.AddMonths(-20);
    }
}

LoginMe 方法

首先,让我们关注单击 Login 按钮时触发的 LoginMe 方法。在此方法中,在设置常规 Session 变量后,我们创建一个 GUID(一个唯一且几乎不可能猜测的值),并将其保存为名为 AuthToken 的新 Session 变量。然后,相同的 GUID 被保存到名为 AuthToken 的 cookie 中。

LogoutMe 方法

LogoutMe 方法中,我们首先显式使 ASP.NET_SessionId cookie 过期,以确保在用户单击 Logout 按钮时从浏览器中删除此 cookie,然后,我们也使 AuthToken cookie 过期。

Page_Load 事件(在实际应用程序中,请将此逻辑保留在 Master Page 的 Page_Load 方法中)

Page_Load 事件中,我们检查常规的 LoggedIn Session 变量,同时,我们还检查名为 AuthToken 的新 Session 变量以及名为 AuthToken 的新 Cookie。如果所有三个都不为 null,那么我们再次匹配新的 Session 变量 AuthToken 和新的 Cookie AuthToken 的值。如果两者**不**相同,则我们显示一条失败消息(在实际应用程序中,将用户重定向到登录页面)。

此逻辑可确保即使 ASP.NET_SessionId cookie 的值被攻击者知晓,他也无法登录应用程序,因为我们正在检查由我们创建的新 Session 值和新 Cookie(其 GUID 值也是由我们创建的)。攻击者可以知道 Cookie 的值,但他/她无法知道存储在 Web 服务器级别的 Session 值,并且由于此 AuthToken 值在用户每次登录时都会更改,因此旧值将不起作用,攻击者甚至无法猜测此值。除非新的 Session(AuthToken)值和新的 Cookie(AuthToken)相同,否则没有人能够登录应用程序。

输出

ASP.NET_SessionId 配合 AuthToken cookie

Itfunda_6193_3.JPG

希望您觉得这篇文章很有趣,感谢您的阅读。订阅我的 RSS feed 以定期阅读更多文章。

最初发布于 这里

© . All rights reserved.