ASP.NET Identity is extremely complex and overkill for what I need in an ASP.NET MVC 5 app. Keeping things simple is a good principle IMHO.
When a user logs in I simply use Dapper to lookup email & password in the database (hashing & salting used).
Then I set a session["userID"] = user.Id // from database.
This Id is a high entropy randomly generated string (ie a security stamp that could be revoked - not the actual user ID), and the session timeout is set to a long time - eg 1 month - so users don't have to keep logging in.
Where a user is an administrator I also just set Session["admin"] = true;
I then create a simple class that inherits from AuthorizeAttribute:
public class MyAuthorizeAttribute : AuthorizeAttribute
{
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
if (httpContext.Session["userID"] == null)
return false;
else
return true;
}
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
filterContext.Result = new RedirectResult("~/Account/Login");
}
}
so I can use authorization attributes on my controllers:
[MyAuthorize]
public ActionResult MyController()
{
...
}
Is this secure? Are there any steps I am missing to secure this - if so what?
Is something so wrong with this it will fail in operation?
Is session capable of doing this? If not what is & what would be a better way (that is just as simple)?
First of all I don't agree with your comments about complexity. So far ASP.Net Identity is the most comprehensive and well-composed authentication/authorisation framework for .Net. It is superior to all predecessors with a lot of possible extensibility points. If you think this is very complex, then think of a security in general - it is a very complex domain with high risks involved. Identity framework actually simplifies and hides a lot of complexity so you don't have to deal with this. If you still insist this is complex... well, yes, software development is a complex business. Computing in general is complex... well, life is complex!
Anyway, back to your solution. I can see at least one major flaw with your solution. You keep your users authenticated by session cookie. You propose the session to be a very long lived. This means that session cookie is not changed for a long time. So in case somebody have managed to gain access to the session somehow, they will have a very long window of opportunity to abuse the access to your system as somebody else.
Identity solves this problem by rotating the cookie - by default every 30 minutes the auth cookie value is changed. So if you get somebody's yesterday cookie value - it won't be any good. You can also reduce this value to couple minutes or even make the cookie value update on every request.
Then you'll need to think about session expiry and management. First this will be a memory hog - for every user logged in the last 30 days you'll have to maintain a live session on your server. Imagine a thousand users logged on... I don't know how much data you'll have per user session, but given enough use you'll start hitting memory problems. Then if you would want to load-balance your servers, you'll have to introduce a sticky session. Also when your server restarts, all your users will loose their sessions (I've used this kind of system - it is horrid).
Next thing you'll have to take care of multiple user logins - what will happen when the same user logs in from 2 different browsers? What happens to user sessions when user changes password - how does it log out other browsers?
I've not done a terrible amount of work with session, so I'm not sure how session cookie is created. But looking on documentation (Session Identifiers section) sessionId is stored in the cookie. And you propose to put User.Id into that session cookie. That means that if User.Id is known to an attacker, then they can log in as that user any time they like. To mitigate that you'll have to come up with the way to hide user id inside the session and use some sort of correlation id, and even then you are not escaping the fact that once created correlation id will last entire session life - 30 days. Talking about complex solution?
Identity provides you a lot of functionality out of the box. Things that you probably won't need now but might in the future. And with home-grown solution this will be a case of writing this yourself.
Validate newly registered emails
2-Factor Authentication
Social logins
Password strength requirements
Username/email uniqueness requirement
User Lockout
Mitigate password sharing
Password reset through email link
List is not exhaustive - just something from top of my head.
Related
I'm designing a system, where the admin will be able to login as a user to fix things on their behalf etc. I'd like it so they have an additional role during this period. Is there any way to add the role in memory or in a way that ends when they logout/close the browser. I could add the role from the admin screen and remove when that user logs in again but it could easily go wrong. Cheers.
This isn't about how to do impersonation. I've got that part working. I'd like to be able to add an additional role to the user but only when they are being impersonated (so there are a few extra diagnostic screens available). I think the person below is answering my question by explaining that when I add a claim, I'm adding it to the the cookie. I was thinking adding this information persisted back to the database. I will try that code tomorrow but I suspect it is the direction I need to go in. This is silly question but have the rules changed recently, I've noticed tonight people being a little enthusiastic to correct grammar etc.
ASP.NET Core 2.0 Identity uses claims based authentication. Each role is a claim. Claims are persisted for the session via several means but generally in the application cookie issued when they log in or JWT auth tokens (not in memory).
Using the SignInManager creating a user principal and adding an extra claim should be pretty trivial:
// create the user principal
var principal = await signInManager.CreateUserPrincipalAsync(user);
// add the extra role
principal.Identities.First().AddClaim(new Claim(ClaimTypes.Role, SomeRole));
// issue the application cookie
await HttpContext.SignInAsync(principal)
net mvc 5 application using entity frame work etc and am new to .net c# etc (used to php & sessions)
so i have read allot about using .nets authentication service and that is some how registers a user upon login using FormsAuthentication.SetAuthCookie.
however i need to authenticate a user group for example admin or moderator. and from what i understand this can be achieved and be set using [authenticate(roles="admin")].
but surely if this is using a set cookie a user if they knew how could just change their registered role from user to admin to access restricted content?
so in as simple terms as possible how does .net mvc ensure security in authenticating users? can i use sessions instead of cookies? do i need to create my own authentication system.?
i have searched and read all i can find and most resources just explain how cookies work or how to implement authentication using cookies but very little about sessions.
I'll try to be as concise as possible:
Yes, ASP.NET MVC 5 uses cookies out of the box (if you chose Individual User Accounts in the project wizard)
The authorization of a group or role by means of an [Authorize(Roles="bla")] attribute to decorate controllers and/or controller methods will do just that. It's as if you would be writing
if(!User.IsInRole("bla"))
{
return new HttpUnauthorizedResult();
}
else
{
//here's your ultra-secret View
return View();
}
What if a user changes role while in-session or if he or she has a persistent cookie?
Indeed, you'll need to handle the interval between role change and cookie update.
Read up on it here
Long story short: the design decision is yours whether you think it better to log off a user when re-assigning roles or to make db roundtrips at every authorization check.
Can you use a session variable like in PHP? Sure (the Session object exists), but you shouldn't.
If and when the situation arises where you absolutely NEED to pass some arbitrary data however, there's ViewBag, ViewData and TempData.
I won't go as far as to say, that these constructs are superfluous, they certainly have their use from time to time, but do try and design your application to maximize the use of strongly-typed models, viewmodels and make use of the REST-based url architecture to get or put your data.
I've got a ASP.NET MVC web app which uses forms authentication.
I'm using ActiveDirectoryMembershipProvider to validate users against our domain.
if (Membership.ValidateUser(m.Username, m.Password))
{
FormsAuthentication.SetAuthCookie(m.Username, true);
....
This means the user gets validated only when they log in.
Problem with that is ofcourse that if the user's password changes they still remain logged in. Or worse, user leaves our company with a grudge, and they still have access.
I would have thought such a simple use case would have an obvious answer but I've been stuck on this for a while now.
I could put the users password in the session and then validate it every time, but that doesn't feel right.
What is the suggested/correct way of handling this?
The typical solution is to force log out when users unsubscribes from the service or less commonly when they change password. Use this method:
FormsAuthentication.SignOut();
FormsAuthentication.RedirectToLoginPage();
If the user can be deactivated outside of the app (i.e. Active Directory), the typical practice is to rely on the session time-out and perhaps ask for the credentials once more for critical operations. If you absolutely cannot allow the deactivated user to work while the session is still active, then yes, you'll have to check the credentials on every request. Since storing the password in the app is a very bad idea, it means you'll have to ask for credentials on each request which arguably is an even worse idea.
As for the password change, it normally doesn't modify the user's permissions so it should be harmless to allow for them to continue working.
The answer is to periodically (every 30 minutes or so) check User.IsApproved and User.LastPasswordChangedDate to make sure the users credentials are still valid.
To do this you need to manually create the FormsAuthenticationTicket and cookie, rather than using FormsAuthentication.SetAuthCookie.
Put the date you validated the user inside UserData and compare this against LastPasswordChangedDate.
I've implemented this and it works perfectly.
More information here
Check if Active Directory password is different from cookie
ok so, i have this dilemma on how i should save login credentials in mvc at the same time avoid as much hit on the database. i know i can easily use Forms Authentication to save a User instance but is it advisable?
At the moment the way I do it is I store the User Id in a cookie which i then would access everytime an Action gets called that would "require" a login access. Before the action gets accessed the User Id will be used to retrieve a "New" User instance. This will be the same on every Action, I don't store the User in the cookie as I feel like once the cookie is compromised everything about the User shall be available for the hacker (Userid, email, roles, etc)
So if i have a ton of actions that would require a login that will be difficult on my bandwidth. What do you think of the method I'm using? Should I change it to have all the User object be stored in the cookie with a short timeout? Any ideas are greatly appreciated.
thanks!!
It seems like you are trying to address a bandwidth issue. That alone would suggest that you shouldn't store more than you have to (ie: session id) in the cookie.
There are two major problems (among others) for using cookies.
1) They are sent up on every request
2) There is only limited amount of information you can store.
In general, trusting anything the user gives you (that includes encrypted cookies) is bad.
How many concurrent users do you foresee having on your website? Keep in mind that the database will be able to cache certain calls. Furthermore, if you are using a ORM like nhibernate, you will get 2nd level caching there. If all else fails, could you use the in-memory session management?
The biggest problem I have with putting userid's in the cookie is the entropy of that key. Say your userId is an email. All I have to do as an attacker is guess a userid that is valid in your system, and I will "automatically" become that user. The reason why people use sessionID's and then retrieve the user is that in theory sessionID's are harder to guess.
My suggestion would be to use database session management if you are in a load balanced situation. If not, use in-memory. It is fast. Memory is cheap. And unless you are storing 10's of mb of data in session for each user, and you have 10000's of users, you should be fine.
As Ken stated, you should probably be using the standard [authorize] tags available with MVC as opposed to creating your own method.
It sounds like you pretty much implemented form based authentication and something comparable to the [Authorize] attribute.
So if i have a ton of actions that would require a login that will be difficult on my bandwidth
Forms Authentication uses a cookie and is baked into the system. If you don't want to store your user information in SQLServer there are plenty of other options.
It sounds like you are trying to implement something that is already done. In my opinion, let's leave the security stuff to people that know about security. I would suggest working within the framework provided unless you have proof that you solution needs something else!
There is a UserData property on the FormsAuthenticationTicket object that could be used to store additional data other than the Username.
I had a project that had a similar need. I stored the values as a NameValueCollection encoded like a query string:
"email=myemail#some.com&roles=Somebody&roles=Special"
(there's also a handy HttpUtility.ParseQueryString() method that is useful for getting the values back out of the UserData property)
You can use the FormsAuthentication.Encrypt and FormsAuthentication.Decrypt to convert the ticket to and from the Cookie value.
Im using autologin on my MVC 3 website.
How do I best handle this problem:
A user signs in at his own computer (and gets a 30 day cookie)
Same user signs in at a friends computer (and gets a 30 day cookie)
Its now possible to autologin in at both computers. The user realizes this and changes his password but his friend is still able to autologin from his computer until the cookie expires.
How do I best handle this?
I could of course set at date on the user when password changed and check this up against the date in the cookie.
Or am I missing something?
I know what you're saying, but I think you're implying an association between the "remember me" function and the "password change" function which in practice, isn't there. The auth token you get when authenticating is not generally tied to the value of the password (i.e. when using the membership provider), after all, you're logically keeping the identity authenticated across sessions and in this regard, it works just fine.
To be honest, this sounds like more of a user behaviour problem than a technology problem. In your use case, someone is consciously asking the browser to allow them to remain authenticated for a long period of time and doing so on a machine which they have no control over. Of course I'm assuming you have a "remember me" checkbox and if you don't, there's your answer right there.
The other thing you might want to look at is what OWASP talks about in part 3 of the Top 10 - Broken authentication and session management. This link will put it in a .NET context for you but in short, it talks a lot about reducing the opportunity for exactly what you're describing to happen by things like eager session expiration, disabling sliding sessions and obviously giving end users the control to expire the token at session expiration and log out at any time.
Don't yo have Remember me checkbox on your login form. The value of this checkbox will dictate whether you are going to create persistent cookie or not. if you don't create persistent cookie, it will expire as soon as session ends. In this scenario you user can leave Remember me checkbox unchecked when logging in on his friends computer. If he doesn't he is calling for trouble himself.