31
votes

An external company has done some penetration tests on the ASP.NET MVC 5 application i'm working on.

An issue that they raised is described below

A cookie linked with session Management is called AspNet.ApplicationCookie. When entered manually,the application authenticates the user. Even though the user logs out from the Application,the cookie is still valid. This means,the old session cookie can be used for a valid authentication within unlimited timeframe. In the moment the old value is inserted, the application accepts it and replaces it with a newly generated cookie. Therefore, if the attacker gains access to one of the existing cookies, the valid session will be created,with the same access as in the past.

We're using ASP.NEt Identity 2.2

Here's our logout action on the account controller

 [HttpPost]
    [ValidateAntiForgeryToken]
    public ActionResult LogOff()
    {
        AuthenticationManager.SignOut();
        return RedirectToAction("Login", "Account");
    }

in startup.auth.cs

 app.UseCookieAuthentication(new CookieAuthenticationOptions
        {
            AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
            LoginPath = new PathString("/Account/Login"),
            ExpireTimeSpan = TimeSpan.FromHours(24.0),
            Provider = new CookieAuthenticationProvider
            {
                // Enables the application to validate the security stamp when the user logs in.
                // This is a security feature which is used when you change a password or add an external login to your account.  
                OnValidateIdentity = SecurityStampValidator
             .OnValidateIdentity<ApplicationUserManager, ApplicationUser, int>(
                 validateInterval: TimeSpan.FromMinutes(1.0),
                 regenerateIdentityCallback: (manager, user) =>
                     user.GenerateUserIdentityAsync(manager),
                 getUserIdCallback: (id) => (Int32.Parse(id.GetUserId())))

            }
        });

I would have thought that the framework would have taken care of invalidating an old session cookie but browsing through the Owin.Security source code it appears not.

How do i invalidate the session cookie on logout?

edit on Jamie Dunstan's Advice i've added AuthenticationManager.SignOut(DefaultAuthenticationTypes.ApplicationCookie); but then has made no difference. I can still still log out of the application, clone a previously authenticated request in Fiddler, and have it accepted by the application.

Edit : My updated Logoff method

 [HttpPost]
    [ValidateAntiForgeryToken]
    public async Task<ActionResult> LogOff()
    {
        var user = await UserManager.FindByNameAsync(User.Identity.Name);

        AuthenticationManager.SignOut(DefaultAuthenticationTypes.ApplicationCookie);
        await UserManager.UpdateSecurityStampAsync(user.Id);

        return RedirectToAction("Login", "Account");
    }
3
Have you tried replacing AuthenticationManager.Signout(); with AuthenticationManager.Signout(DefaultAuthenticationTypes.ApplicationCookie);? The parameterless signout appears to be a bit inconsistent. - Jamie Dunstan
Cheers. Done, but i can still log out of the application, and then clone a previously authenticated request in fiddler and have it accepted - MrBliz
I've also removed the Expiretimespan from startup.Auth. No difference. - MrBliz
The best idea I can come up with is to manually call UserManager.UpdateSecurityStampAsync(userId); after calling SignOut. Can you try this and see if it works? - Jamie Dunstan
That did not work. Still able to use a previously authenticated request via fiddler after the user has logged out. - MrBliz

3 Answers

10
votes

Make sure you use AuthenticationManager.Signout(DefaultAuthenticationTypes.ApplicationCookie); as correctly suggested by Jamie.

Being able to login with the same cookie again is by design. Identity does not create internal sessions to track all logged-in users and if OWIN gets cookie that hits all the boxes (i.e. copies from the previous session), it'll let you login.

If you still can login after the security stamp is updated, most likely OWIN can't get a hold of ApplicationUserManager. Make sure you have this line just above the app.UseCookieAuthentication

app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);

Or if you are using DI take ApplicationUserManager from DI:

app.CreatePerOwinContext(() => DependencyResolver.Current.GetService<ApplicationUserManager>());

Also reduce the validateInterval: TimeSpan.FromMinutes(30) to lower value - I usually settle for couple minutes. This is how often Identity compares values in auth-cookie to the values in the database. And when the comparison is done, Identity regenerates the cookie to update timestamps.

0
votes

Trailmax's answer is spot on, I thought I would add that if someone is trying to do this while also using ASP.NET Boilerplate, the following is what I used to make this work:

app.CreatePerOwinContext(() => IocManager.Instance.Resolve<UserManager>());

I originally had:

app.CreatePerOwinContext(() => IocManager.Instance.ResolveAsDisposable<UserManager>());

and is wasn't working.

0
votes

you were on the right way. Indeed the easiest way is to update user SecurityStamp but normally executing it doesn't lead to success because actualy credentials aren't changed and it remains the same in db. Solution, try this:

private string NewSecurityStamp()
        {
            return Guid.NewGuid().ToString();
        }

private async Task RegenerateSecurityStamp(string userId)
    {
        var user = await _userManager.FindByIdAsync(userId);
            if (user != null)
            {
                user.SecurityStamp = NewSecurityStamp();
                await _userStore.UpdateAsync(user);
            }
    }

[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> LogOff()
    {
        AuthenticationManager.SignOut(DefaultAuthenticationTypes.ApplicationCookie);
        await RegenerateSecurityStamp(User.Identity.GetUserId());
        return RedirectToAction("Login", "Account");
    }