Invalidate Old Session Cookie - ASP.Net Identity - asp.net-mvc

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");
}

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.

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.

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");
}

Related

Problem with Claims in ASP.NET Core (user still have some claim after updating database)

I'm learning asp.net core and I'm stuck. I'll try to explain somehow.
Problem is with user claim.
When I log in into website, user have, let's say Create and Delete Claim and all working.
But when THAT user changes that HE cannot Delete something, after updating database HE still can.
In database Delete is gone (which is good).
Authorization is checked by authorization attribute:
[Authorize(Policy="DeletePolicy")]
I found something what may help.
ClaimsPrincipal User
property still have both claims (Create and Delete), but when I check DB:
await userManager.GetClaimsAsync("user_id")
I get only Create Claim which is good.
My question is: what precisely is that ClaimsPrincipal User property and why is not updated automatically?
Do I need to update User's Claims manually?
Claims are embedded in authentication cookie. It's like a snapshot of user's claims at a moment when user was signed in. Instead of putting all claims into cookie you can create ExtraClaimsMiddleware and put it after authentication middleware:
public class ExtraClaimsMiddleware
{
private readonly RequestDelegate _next;
public ExtraClaimsMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task InvokeAsync(HttpContext context,
YourDb db)
{
//get additional claims for the current user from database, cache it if you want
var userId = context.User.FindFirstValue(ClaimTypes.NameIdentifier);
var extraClaims = await db.GetExtraClaimsAsync(userId);
foreach (var claim in extraClaims)
{
context.User.Identities.First().AddClaim(new Claim(claim.ClaimType, claim.ClaimValue));
}
await _next(context);
}
}
public static class ExtraClaimsMiddlewareExtensions
{
public static IApplicationBuilder UseExtraClaims(this IApplicationBuilder builder)
{
return builder.UseMiddleware<ExtraClaimsMiddleware>();
}
}
Then in Startup.cs:
app.UseAuthentication();
app.UseExtraClaims();
I am not sure if I understand your problem correctly but I guess it can be solved with the SignInManager Class.
After you deleted the claim from the database you should be able to use the “RefreshSignInAsync”-Method to update the user´s cookie.

How to properly log in a user from Owin Middleware

I'm making my own system to authenticate jwt tokens in certain scenarios.
When I have properly validated the token, I have
var userIdentity = await user.CreateIdentityAsync(DefaultAuthenticationTypes.ExternalBearer);
owinContext.Authentication.User = new System.Security.Claims.ClaimsPrincipal(userIdentity);
owinContext.Authentication.SignIn(userIdentity);
System.Web.HttpContext.Current.User = owinContext.Authentication.User;
await next()
but that doesn't seem to fix authentication which still fails at - I believe - the Asp.Net Mvc level. Because I know it uses HttpContext I try adding this before calling next()
HttpContext.Current.User = new GenericPrincipal(userIdentity, new string[0]);
This gets me further along but I still seem to be getting an an authorization error it would seem (by searching source for the message that I get and where its used) to be coming from the Web Api [Authorize] attribute.
I'm hitting a wall as far as tracing through the .net source code. The only way I should be getting this message is if IsAuthorized returns false. But there are no roles nor users specified (it's just plain [Authorize]) and before heading off to the next() I can stop the debugger and check that yes there is a user identity, and yes it IsAuthorized.
I've overridden the AuthorizeAttribute so as to place breakpoints and can see that by the time it is called however, my actionContext is associated with a completely different identity with IsAuthorized == false. Which in turn makes me wonder if I'm signing in the user identity wrong
So... am I doing this correctly? What should I be doing?
I have never undertstood why but in my case, i have need to valid the ticket after signing in:
var userIdentity = await user.CreateIdentityAsync(DefaultAuthenticationTypes.ExternalBearer);
ctx.Authentication.SignIn(userIdentity);
AuthenticationTicket ticket = new AuthenticationTicket(userIdentity, null);
ctx.Validated(ticket);
Edit
I'm not really in the same context. In my case, I have a custom authentication provider inheriting of Microsoft.Owin.Security.OAuth.OAuthBearerAuthenticationProvider :
public class CustomBearerAuthenticationProvider:OAuthBearerAuthenticationProvider
{
public CustomBearerAuthenticationProvider() : base()
{
this.OnValidateIdentity = (context) => Task.Run(() =>
{
var identity = this.CreateApplicationIdentity(user);
context.OwinContext.Authentication.SignIn(identity);
AuthenticationTicket ticket = new AuthenticationTicket(identity, null);
context.Validated(ticket);
});
}
}
context is of type : Microsoft.Owin.Security.OAuth.OAuthValidateIdentityContext

Using cookies to stay signed in with third party login providers and Microsoft.AspNet.Identity.Owin 2.0

I've followed this tutorial in an attempt to use several third party login providers with a simple ASP.NET MVC SPA application I am writing. While configuration is simple enough (I've actually enabled Twitter and Microsoft), and the sign-in process works correctly, the user credentials are stored in a browser session cookie only and do not persist across browser sessions.
I've also tried using the alpha-1 sample project from NuGet (with the same basic configuration applied) and it also does not work (at least in my environment).
The web application is only hosted locally (as I do not have an Azure account in which to test).
I thought the setting ExpireTimeSpan would affect it, but it does not:
// Enable the application to use a cookie to store information for the signed in user
app.UseCookieAuthentication(GetCookieAuthenticationOptions());
private static CookieAuthenticationOptions GetCookieAuthenticationOptions()
{
var options = new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
CookieSecure = CookieSecureOption.SameAsRequest,
SlidingExpiration = true,
CookieName = "MYSECURITY",
ExpireTimeSpan = TimeSpan.FromDays(45.0),
LoginPath = new PathString("/Account/Login"),
Provider = new CookieAuthenticationProvider
{
OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<ApplicationUserManager, ApplicationUser>(
validateInterval: TimeSpan.FromMinutes(20),
regenerateIdentity: (manager, user) => user.GenerateUserIdentityAsync(manager))
}
};
return options;
}
Cookies (I changed the default name of the cookie intentionally to validate that the code was executing -- it doesn't work with the default either):
The MVC Single Page Application project template in Visual Studio contains the following method in the AccountController which forces all all external logins to not be persistent across browser sessions:
public async Task<ActionResult> ExternalLoginCallback(string returnUrl)
{
// ...
await SignInAsync(user, isPersistent: false);
// ...
}
If you are comfortable with the security implications of trusting an identity that has been authenticated by an external provider across browser sessions, you could set isPersistent = true when calling SignInAsync.
Also be aware that any persistent login will be made non-persistent once the SecurityStampValidator fires the regenerateIdentity callback (which will occur after 20 minutes in your sample code above). See the question ExpireTimeSpan ignored after regenerateIdentity / validateInterval duration in MVC Identity (2.0.1) for discussion on this behavior.

Save claims to db on login

I have an MVC5 application that is using Oauth and is logging in correctly however I want to save the returned email to the database and am having trouble doing this.
I have the following code in startup.auth.cs:
The below code adds the claim fine and I can the email claim in the debugger, however if it is the users first login then the user isn't actually created at this point so I can't update the user here.
LinkedInAuthenticationOptions x = new LinkedInAuthenticationOptions();
x.ClientId = "key";
x.ClientSecret = "key";
x.Scope.Add("r_emailaddress");
x.Provider = new LinkedInAuthenticationProvider()
{
OnAuthenticated = async context =>
{
//Get the access token
context.Identity.AddClaim(
new System.Security.Claims.Claim("Email", context.Email));
}
};
app.UseLinkedInAuthentication(x);
In the below code (in the default project accountcontroller.cs) I try to access the email claim that I added above however it is unable to find the claim...
public async Task<ActionResult> ExternalLoginCallback(string returnUrl)
{
ClaimsIdentity identity = UserManager.CreateIdentity(user, DefaultAuthenticationTypes.ApplicationCookie);
}
Does anybody know how I can access the email claim that I added in startup.auth.cs ?
You want to do something similar to this blog post, just store the email claim instead of the Facebook access token.
How to get more data from external providers?

Intercept Cookie Login in ASP.net MVC 5

I am using OWIN to configure my MVC 5 authentication using
public void ConfigureAuth(IAppBuilder app) {
IAppBuilder.UseCookieAuthentication(AuthenticationType =
DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/Account/Login")
});
}
... and sign on users using
var identity = await UserManager.CreateIdentityAsync(user,
DefaultAuthenticationTypes.ApplicationCookie);
AuthenticationManager.SignIn(new AuthenticationProperties() {
IsPersistent = isPersistent }, identity);
In my User model I have a flag, Inactive, which when set by an admin should prevent the user from logging in. This is not a problem when users login without asking to be remembered (without setting a cookie) since I can retrieve whether they are inactive or not and act based on that.
The problem is when they login and ask to be remembered (ticking 'Remember me' checkbox). The next time they browse the site, ASP.net automatically logs them in. Where should I intercept this procedure to check whether the user is inactive or not?
I read about Application_PostAuthenticateRequest in global.asax
protected void Application_PostAuthenticateRequest(object sender, EventArgs e) {
//do stuff with User.Identity here
}
But this method seem to be called twice, sometimes 3 times. Also I want to be able to see whether the authentication came from a cookie or by entering credentials in the login page.
Thanks,
I would assume you have very few "InActive" users. I would just store them in Cache and check it on every request. If the user happens to be InActive then log them out. In memory is better than relying on cookie information.

Resources