Based on what i have understood we have
sessionId
is stored in the cookie .AspNetCore.Session
Deleting the cookies and Clear
...or is there a way to protect us from Session-Fixing?
Yes there is! OWASP states:
Unfortunately, some platforms, notably Microsoft ASP, do not generate new values for sessionid cookies, but rather just associate the existing value with a new session. This guarantees that almost all ASP apps will be vulnerable to session fixation, unless they have taken specific measures to protect against it.
The same page recommends an approach for ASP.Net, which we used for all of our ASP.Net applications and which passed pen testing. I think it is still valid for ASP.Net Core:
The idea is that, since ASP prohibits write access to the ASPSESSIONIDxxxxx cookie, and will not allow us to change it in any way, we have to use an additional cookie that we do have control over to detect any tampering. So, we set a cookie in the user’s browser to a random value, and set a session variable to the same value. If the session variable and the cookie value ever don’t match, then we have a potential fixation attack, and should invalidate the session, and force the user to log on again.
This is a simplified example of how we approached this in .Net Core Razor Pages and should give you an idea of how to implement it yourself:
public IActionResult OnPost()
{
Login();
return Redirect("~/Login");
}
private void Login()
{
// Check the user's credentials and do all the other necessary stuff.
// ...
// Create the random value we will use to secure the session.
string authId = GenerateAuthId();
// Store the value in both our Session and a Cookie.
HttpContext.Session.SetString("AuthId", authId);
CookieOptions options = new CookieOptions()
{
Path = "/",
HttpOnly = true,
Secure = true,
SameSite = Strict
};
Response.Cookies.Append("AuthCookie", authId, options);
}
private string GenerateAuthId()
{
using(RandomNumberGenerator rng = new RNGCryptoServiceProvider())
{
byte[] tokenData = new byte[32];
rng.GetBytes(tokenData);
return Convert.ToBase64String(tokenData);
}
}
Check the content of the Session and Cookie wherever you need it. If they don't match, you should Clear
the Session (I don't think Session.Abandon
is still available in .Net Core) and log out the user.
public void OnGet()
{
string cookieValue = Request.Cookies["AuthCookie"];
string sessionValue = HttpContext.Session.GetString("AuthId");
if (cookieValue == null || sessionValue == null || cookieValue != sessionValue )
{
// Invalidate the session and log out the current user.
}
}
Session.Clear
only removes all data from the session, it does not actually remove the session itself. That will occur when the timeout is hit. It was an odd choice, in my opinion, for the ASP.NET Core team to not have implemented Session.Abandon
, as previously existed, since that actually would remove the actual session, itself.
As long as the actual session still exists, even if the data for it no longer does, it can still be retrieved by that session id, as a result, the problem shifts to the client-side.
Importantly, the server cannot actually make the client do anything. Calling Cookies.Delete
really only sends a new Set-Cookie
response header for the same cookie with an expiration date in the past. This should prompt the client (browser, most likely) to then remove that cookie, since it is now expired. However, that is totally 100% on the client, so if there's a bug or the client otherwise is not picking up the change or the client simply refuses to comply for whatever reason, the cookie will remain. Then, again, if the cookie still exists and the session identified by the session id it contains still exists, it can be restored.
Long and short, the code you have should work, and there's really not anything else you can do other than what you're already doing. If the session isn't being abandoned, there's some other issue somewhere (most likely with the client).