Authentication ASP.NET_SessionID (Session) along with AuthToken (GUID) cookie
Introduction
ASP.NET Session keeps track of the user by creating a cookie called
ASP.NET_SessionId
in the user browser. This cookie value is checked for every request to ensure that the data being served is specific to that user. In many applications, a Session variable is used to track the logged in user, i.e., if a session variable exists for that user, then the user is logged in, otherwise not.Background
Whenever any data is saved into the Session, the
ASP.NET_SessionId
cookie is created in the user’s browser. Even if the user has logged out (means the Session data has been removed by calling the Session.Abandon()
or Session.RemoveAll()
or Session.Clear()
method), this ASP.NET_SessionId
cookie and its value is not deleted from the user browser. This legitimate cookie value can be used by the hijacker to hijack the user session by giving a link that exploits cross site scripting vulnerability to set this pre-defined cookie. When the user clicks this link and logs in, the user will have the same ASP.NET_SessionId
cookie value that hijackers knows and he will also be able to browse the user account and will be able to access all the information pertaining to that user. This attack is called Session fixation vulnerability.
Let’s create a demo application that shows the existence of the
ASP.NET_SessionId
cookie even if the user has logged out and all Session data has been removed.
ASPX Page
<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>
In the above code snippet, we have two
TextBox
es, two Button
s, and a Label
control.
Code-behind
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();
}
On click of the Login button, the
LoginMe
method fires that creates Session[“LoggedIn”]
after validating theTextBox
es' value.
On click of the Logout button, we call the
Session.Clear()
, Session.Abandon()
andSession.RemoveAll()
methods to ensure that the session variable is removed.Output
The ASP.NET_SessionId cookie when user is logged in
Notice in the below image that when the user has logged in, an
ASP.NET_SessionId
cookie has been created.
After clicking on Login, go back and refresh the page.
Now when we click on the Logout button, even if the Session has been abandoned / removed, the
ASP.NET_SessionId
cookie exists.ASP.NET_SessionId cookie even if user is logged out
After clicking on Login, go back and refresh the page.
How to fix this vulnerability
Simple fix
To avoid Session fixation vulnerability attacks, we can explicitly remove the
ASP.NET_SessionId
cookie in the Logout method.Bullet proof fix
To bullet proof this attack, we can create another cookie (e.g.,
AuthCookie
) with a unique value and the same value can be stored into the Session as well. On every page load, we can match this cookie value with the Session value; if both matches, then let the use enter the application otherwise redirect to the Login page.
In the Logout function, ensure that you are removing this new Cookie “
AuthCookie
” as well. To remove this cookie, simply set its expiration date time to a few months earlier than the current date time.
So my modified code-behind for this page looks like below:
Modified code-behind
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 method
First, let's focus on the
LoginMe
method that fires on the click of the Login button. In this method, after setting the normal Session variable, we create a GUID (a unique value and almost impossible to guess) and save it as a new Session variable called AuthToken
. The same GUID is then saved into a cookie named AuthToken
.LogoutMe method
In the
LogoutMe
method, we first explicitly expire the ASP.NET_SessionId
cookie to make sure that this cookie is removed from the browser when the user clicks on the Logout button, and after that, we expire the AuthToken
cookie as well.Page_Load event (In real time applications, keep this logic in the Master Page Page_Load method)
In the
Page_Load
event, we check for the normal LoggedIn
session variable and along with that, we also check for the new Session variable called AuthToken
and the new Cookie AuthToken
. If all three of them are not null, then again we match the new Session variable AuthToken
and the new Cookie AuthToken
values. If both arenot the same, then we write a failure message (in a real time application, redirect the user to the Login page).
This logic makes sure that even if the
ASP.NET_SessionId
cookie value is known to the hijacker, he will not be able to login to the application as we are checking for the new Session value with the new cookie that is created by us and their GUID value is created by us. A hijacker can know the Cookie value but he can’t know the Session value that is stored in the web server level, and as this AuthToken
value changes every time the user logs in, the older value will not work and the hijacker will not be able to even guess this value. Unless the new Session (AuthToken
) value and the new Cookie (AuthToken
) are the same, no one will be able to login to the application.
Comments
Post a Comment