Asp.Net Identity with 2FA: List of Trusted Browsers - c#

I'm working on a project with Asp.Net MVC 5 and Asp.Net Identity and I'm using two factor authentication. For the login I use:
var result = await SignInManager.TwoFactorSignInAsync(model.Provider, model.Code, isPersistent: model.RememberMe, rememberBrowser: model.RememberBrowser);
which is the default code that came with the new project. However, I also need the ability for a user to "trust" or "remember" a browser, similar to how banks can indicate if this was the first time you have signed in from a particular browser/pc.
My question is around the RememberBrowser property on the sign in method and what .NET Identity does with this data. I want the list of saved browsers and the ability to revoke access to one/and-or all of them. Is that possible within the Identity framework? Also, can I tell if a browser has been "trusted" before by some type of lookup?
Edit:
Maybe it's a good idea to save the browser info in the database and check on login instead of the cookie? That way it can be shown as a list with the ability to delete it. What I'm looking for is what to save and how to integrate it with the Asp.Net Identity without having a security risk.
Edit 2
Here's an example from a website that is already using this:
Edit 3
Maybe this can be implemented as another step for authentication. So basically we'll have a 3 factor authentication:
First user logs in with user/pass
Then we'll check if the 2FA is enabled and get the code if necessary
We get the user's aser agent and IP and check the database if it's new. Then notify if necessary.
So I'm guessing an new cookie should be added to save browser's info. However, we should be able to invalidate this cookie along with the 2FA cookie.

RememberBrowser sets a cookie that allows the 2FA step to be skipped. There is no central way to track this though it would be easy enough to log, however the results may not be accurate because people can delete cookies manually. There's no way to invalidate it I believe but it doesn't really matter as you can invalidate their session and the user is will be required to login with their password again.

Not sure whether saving browser info adds value as browser info is gonna be same for different users (using same browser and version) unless you save requestor IP as well; and saving requestor IP has too many complications.
How about adding a custom claim to the token if user has set RememberBrowser and then do your logic based on this custom claim? For eg, set a custom claim your_claim_name and set a Guid.NewGuid() to it if RememberBrowser is true. Also save the username, this guid and status flag in database . When a request comes, check whether your custom claim is present, if yes query the table with the custom claim value and username to check whether the entry is still active.
You can either delete the entry or soft delete (set the status) the entry for an user so that when next request comes you can perform your required logic.

Related

How to Logout user from a particular session Identity Server 4, .Net Core?

Using Identity Serve 4 with .Net Core 3.1, razor pages. Also using Cookie Authentication
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
Problem -
In a web application John logged-in 2 times
1st Login on Chrome
2nd Login on edge
So, if John again trying to logged-in on 3rd time on Firefox without logout from previous browsers, then I want to logout John from 1st Login on Chrome forcefully.
I can keep the track of logins in a Session table including Session Id, User Id etc.
But I don’t know how logout user from a particular session using Session Id.
Please help.
Thanks
ASP.NET Core provides an ITicketStore interface which allows you to get control of storing user sessions. Once you provide a class implementing this interface and register it, it will call your class when sessions are being created or verified which you can then store in a database however you like, including attaching arbitrary metadata like browser ID etc.
Now that you have user sessions in your database, you can separately query them and revoke as needed in other logic, including during logins. Since you now provide the session data, simply deleting the record effectively logs the user out from that session. Note that if you use any caching layer to reduce the store requests, you'd need to remove any cached copies as well.
Note that this is separate from IdentityServer and happens with ASP.NET Core itself.
This is a good tutorial that helped me implementing this in my app.
A sample of how it looks to register in Startup, where PersistentTicketStore is my implementation:
// Persistent ticket/cookie store to provide durable user sessions
services.AddSingleton<IUserSessionRepository, UserSessionRepository>();
services.AddSingleton<ITicketStore, PersistentTicketStore>();
services.AddOptions<CookieAuthenticationOptions>(CookieAuthenticationDefaults.AuthenticationScheme)
.Configure<ITicketStore>((options, store) => options.SessionStore = store);
Use the End Session Endpoint
The end session endpoint can be used to end a session and trigger a log out
In the log in process you will need to capture the id_token received from authentication and what user it belongs and store it on some dbo.table. You can use this same table to also keep track if a user has logged in more than once.
To log out a user or end a session you will need to pass the ID you saved as a query string parameter called id_token_hint in a GET call as shown below into:
GET /connect/endsession?id_token_hint={id_token}
For reference see the documentation here https://identityserver4.readthedocs.io/en/latest/endpoints/endsession.html#end-session-endpoint
Since you're saying you can keep track of logins, perhaps you should keep track of each session and assign a number somewhere indicating when it was logged in (1 for Chrome, 2 for edge, 3 for Firefox).
Then each time a request is made, you check in your table what the lowest number is (1,2,3 etc), and if the session matches that number, you sign the user out from that session.
await
HttpContext.SignOutAsync(IdentityServerConstants.DefaultCookieAuthenticationScheme);
Since each browser will have their own cookie, you can use the above method.
After signing someone out, the next login can be assigned 4, and if 2 makes a request you log that client out.....
Also see this: https://github.com/IdentityServer/IdentityServer4/issues/736
I have implemented this.
When a user logs in, the session id (IUserSession.GetSessionIdAsync) is manually stored in our database. The previous value of this database field is used to create a logout_token which I send to my clients. You can have look at IdentityServer4.Infrastructure.BackChannelLogoutClient to figure out how to create the token and post.
All this assumes you have backchannel logout implemented ofcourse.

Stop Users Concurrently Signing In Via Different Machines - Identity Server 4

I am using Identity Server 4 with the quickstart UI and a client using Cookie Authentication.
Lets say I have user A on machine A who is currently logged in via the browser. Then user A decides to go on machine B and logs into that one. As it stands, a new session cookie will be issued for user A on machine B as well as machine A.
This is fine, but I want the option to mark particular users with a flag e.g. IsConcurrent and if it is set to true, they will be given the option to either keep their existing session on machine A, or terminate it and start a new session on machine B.
I have done some reading and found references here to updating the security stamp for a user and setting the interval to zero, so it checks the security stamp in the cookie against the stored version. However, this code didn't seem to be inline with Identity Server's code. Also, it it a valid option in this case?
I also found here which mentions storing and checking the value of session IDs, but I'm not sure if this is valid either?
An initial idea was to implement some middleware that obtained the Machine ID and stored it in a table along with the user, but then I was unsure how to take something like this any further.
Any help or advice would be much appreciated.
Assuming cookie based authentication, you can extend the client to validate the user session provided that the client keeps track of user sessions.
For that you can create a session manager where you add a session for the user (sub) after login, this also includes automatic login sessions (SSO). Remove one or all sessions on logout, which should also be updated on back channel logout (LogoutCallback).
Assuming you use middleware, you can consult the session manager there and decide what to do. Make sure that the current session isn't already activated after login. It has to step through the middleware at least once. Some pseudo code to illustrate the idea:
public Task Invoke(HttpContext context, SessionManager sessionManager)
{
if (context.Principal.Identity.IsAuthenticated)
{
var sub = context.Principal.FindFirst("sub")?.Value;
var sid = context.Principal.FindFirst("sid")?.Value;
// User is allowed when the current session is active.
if (!sessionManager.CurrentSessionIsActive(sub, sid))
{
// Rewrite path if user needs and is allowed to choose: redirect to session selector or
// Activate the current session and deactivate other sessions, if any.
if (sessionManager.HasMultipleSessions(sub) && sessionManager.CanSelectSession(sub))
context.Request.Path = new PathString("/SelectSession");
else
sessionManager.ActivateCurrentSession(sub, sid);
}
}
return _next(context);
}
On post of the SelectSession form you can mark in the session manager which sessions are active. If the old session should be preserved, then ignore the old session (remains active) and mark the current session as active.
Make sure to add the middleware after authenticating the user.
Please note that for access tokens you'll need a different strategy.

Session protection protect copying and pasting user identity cookie

I logged in as user 1. and user2 in another browser. I have a user identity session value in cookie. when i copy user1 cookie value and paste it in user2 cookie value user 2 got changed to user1 . How to prevent this?
You cannot prevent exactly this behaviour. That means if someone gets a token to represent a user (a cookie in your case) then he can represent that user. This is called session-hijacking. Think of it as someone finding a key to another house, it's not much preventing that person from entering the house even though it's not his.
It is possible to do some workarounds that big sites like google do: check if it's a new location or a new computer and ask for additional validation for example, but it's probably not a simple feature.
Otherwise you need to protect your tokens as best as you can to prevent them being accessible in an easy way, like suggested in comments by using TLS, having HTTP-olny cookie that cannot be read by javascript, etc, ensuring proper logout that will delete the cookie and so on.

invalidate aspx authentication cookie

I have an asp.net web form. when a user authenticate, it create a Secured cookie called .aspxauth
uppon logout, I call these 2 methods
FormsAuthentication.SignOut();
Session.Abandon()
Problem is that we had penetration test and if I steal the cookie, logout and manually reinsert the cookie, I become loggued in again. So the .aspauth isn't invalidated server side.
I've googled it and I can't find the answer to that security breach.
Microsoft has acknowledged this issue here: https://support.microsoft.com/en-us/kb/900111
They offer several ideas for mitigating this vulnerability:
protect the application by using SSL
Enforce TTL and absolute expiration
Use HttpOnly cookies and forms authentication in ASP.NET 2.0
Use the Membership class in ASP.NET 2.0
Regarding the last one, I'll paste the contents from the site for convenience/preservation:
When you implement forms authentication in ASP.NET 2.0, you have the option of storing user information in a Membership provider. This option is a new feature that is introduced in ASP.NET 2.0. The MembershipUser object contains specific users.
If the user is logged in, you can store this information in the Comment property of the MembershipUser object. If you use this property, you can develop a mechanism to reduce cookie replay issues in ASP.NET 2.0. This mechanism would follow these steps:
You create an HttpModule that hooks the PostAuthenticateRequest event.
If a FormsIdentity object is in the HttpContext.User property, the FormsAuthenticationModule class recognizes the forms authentication ticket as valid.
Then, the custom HttpModule class obtains a reference to the MembershipUser instance that is associated with the authenticated user.
You examine the Comment property to determine whether the user is currently logged in.
Important: You must store information in the Comment property that indicates when the user explicitly signed out. Also, you must clear the information that is in the Comment property when the customer eventually signs in again.
If the user is not currently logged in as indicated by the Comment property, you must take the following actions:
Clear the cookie.
Set the Response.Status property to 401.
Make a call to the Response.End method that will implicitly redirect the request to the logon page.
By using this method, the forms authentication cookie will only be accepted if the user has not been explicitly signed out and the forms authentication ticket has not yet expired.
Read this article about Session fixation and how to get rid of it once and for all:
http://www.dotnetfunda.com/articles/show/1395/how-to-avoid-the-session-fixation-vulnerability-in-aspnet
This remains an issue in .NET Framework. Everyone seems to think Session.Abandon() is the answer, but the sad truth is that command does not invalidate the session on the server's side. Anyone with the right token value can still resurrect a dead session, until the session expires based on the Web.config settings (default = 20minutes).
A similar questioner posed this question a long time ago here:
Session Fixation in ASP.NET
Most of those links are dead, and Microsoft has no new news on the topic.
https://forums.asp.net/t/2154458.aspx?Preventing+Cookie+Replay+Attacks+MS+support+article+is+now+a+dead+link
Worse still, you're still vulnerable to this cookie replay attack even if you're implementing a completely stateless MVC application and don't use the Session object to store data between views. You can even turn off session state in the web.config settings and still replay cookies to gain access to a logged-out session.
The true solution is hack-y and described here, and you need to have session data enabled InProc to use it.
When the user logs in, set a boolean value in the session data, like Session["LoggedIn"] = true;, which is stored on the server side.
When the user logs out, set that value to false.
Check the session value on every request--an attacker trying to replay a session isn't going to be nice to you and come in through the Login page only. It's probably easiest to do this using a custom filter and registering it globally in the global.asax file (so you don't have to duplicate the code everywhere, or attribute every controller/method).
Even if the attacker has all the cookie values, they won't be able to re-use that same session ID, and the server will automatically delete it once it reaches the specified timeout.
if you are using the FormsAuthentication, you can use this code. By using this code you can destroy the created cookies by setting the Expire property of HttpCookie. It will help you:
FormsAuthentication.SignOut();
Session.Clear();
Session.Abandon();
Session.RemoveAll();
// clear authentication cookie
HttpCookie httpCookie = new HttpCookie(FormsAuthentication.FormsCookieName, "");
httpCookie.Expires = DateTime.Now.AddYears(-1);
Response.Cookies.Add(httpCookie);

Update user's membership role

I am using asp.net MVC 5 identity 2.0
The administrator is able to change user’s role but used must re-log to see the changes. First thought was to re-log user manually but I failed. After that I thought of dynamically changing user’s role or something else. Could you provide me the right way?
I set user’s role using UserManager.AddToRolesAsync
I have tried a lot of things like:
var memberUser = Membership.GetUser(user.UserName.ToString());
if (memberUser.IsOnline)
{
FormsAuthentication.SignOut();
}
or also try to clean up my cookies.
I dunno how I can sign out another user.
Also I have read articles like these
http://w3facility.org/question/mvc-5-addtorole-requires-logout-before-it-works/
How do I forcefully propagate role changes to users with ASP.NET Identity 2.0.1?
How to force logout user when his/her username is changed by another user?
ASP.net Identity 2.0 Sign-out another user
Have a look at the answer provided by Hao Kung on this post he describes exactly how to solve this using the SecurityStamp .
https://stackoverflow.com/a/19505060/1454538
So the primary purpose of the SecurityStamp is to enable sign out
everywhere. The basic idea is that whenever something security related
is changed on the user, like a password, it is a good idea to
automatically invalidate any existing sign in cookies, so if your
password/account was previously compromised, the attacker no longer
has access.
In 2.0.0 we added the following configuration to hook the
OnValidateIdentity method in the CookieMiddleware to look at the
SecurityStamp and reject cookies when it has changed. It also
automatically refreshes the user's claims from the database every
refreshInterval if the stamp is unchanged (which takes care of things
like changing roles etc)
This should get you going.

Categories