How can I remember a user that is logged in without having a security issue? Currently, I am storing the id and a guid into two different cookies and compare them when there is no session alive. If it match then I re-create the session. Btw, both id and guid are nore encrypted.
Is this method safe enough, or should I follow a rather distinct way?
Since cookies can be easily stolen by use of XSS, does not matter if you place information in just one cookie or two, because a theft will take them all.
I have solved with a more complex procedure: I place one encrypted cookie divided in 2 parts (but 2 cookies will work as well), one fixed and the other variable each session. Both are saved also on server side to check: they are only session identifiers, no sensible information contained in the cookie, the correspondence with the user id is saved on the server.
If a fake user enters the account with a stolen cookie, the variable part (or cookie) will change, so when real user connects again he will not be able to enter and you will have the proof that an unauthorized access occurred: then you can delete the permanent session on server side (defined by the fixed part or cookie) in order to avoid any further access by the theft. The real user will re-login and create a new permanent session with a new cookie. You can also warn him that you saw a security flaw, suggesting him to reset password (operation that should never be accessible directly from cookie) or to use another browser for unsafe navigation.
If you save additional user information (user-agent, IP by location) you can check cookie validity also on them in order to avoid even the first entrance of the fake user.
I would recommend you using forms authentication for tracking logged in users in an ASP.NET application.
Related
Sorry for my bad English. I'm writing an application in ASP.NET Core using Vue.JS for client-side. For authenticate user I'm using JWT and ASP.NET Identity. I have a method for change the password. But I can't understand: How to invalide token after password change? I want that the user authenticated in another browser will logout after that. Is there a man who haved a problem like this?
You normally don't invalidate JWT's because they are meant to be short-lived access tokens and therefore after the password change, request for new token will prompt the user to reenter credentials.
If you do absolutely need to invalidate the JWT immediatelly after password change - you need to look into Introspection where your backend api essentially has a backchannel to your token issuer and it can then re-validate token every request. This way if you invalidate token at the issuer side - it will reflect on the api side immediately.
I've been thinking about this and the inability to invalidate a JWT that's already out there may not be built into anything, but is possible.
Here's the narrative: You have an alarm system installed that can be controlled via web and your ex-S/O is logged in to your previously shared account. They are upset and they keep enabling the alarm at random times.
If the web app uses JWTs to store session, you could change your password but the JWT your ex possesses will still be usable for a period of time until the timeout is reached.
Solution 1: short timeout. but what if you want to stay logged in for longer periods (such as a password manager)
Solution 2: logout ALL users by changing the Signing Key of your Certified Authority, basically invalidating ALL JWTs across the board. This is still a less ideal route as I'm sure you can imagine.
Solution 3: track the current JWT for each user in your Users table. If the JWT they possess is different from the current one, then they aren't authenticated. If the user logs out, nullify the stored JWT-data in your Users table which would equally unauthenticate JWTs for that user and force a relogin.
I'd also recommend storing a bool of "logged in" for the user. DO NOT RELY ON THIS. This would be a value to set to true when they log in, set it to false when they log out, and validate the value is 'true' if they ever pass you a JWT. This will ensure that the moment they logout they are forced to reauthenticate.
Assuming you go with solution 3:
When storing JWT data for this solution, I'm leaning towards not storing the entire JWT because it's rather large text to begin with. Alternatively just store the JWS (JWT Signature) which will make the stored value both smaller and unusable if captured for any reason.
Next, it's a hash to begin with so we could just store the last maybe 9 values (9 because int32 max is 2147483647). We just need a bit of uniqueness, not much.
Next, we could avoid the string comparison for validating that the JWS passed is the active one if we use regex to pull the integers out of the JWS and again take maybe the first 9 numbers you encounter.
Following this method, and returning to the narrative, if you were to log out your user would be marked as logged out resulting in both yourself and your S/O being required to reauthenticate. (assuming you've changed your password you're golden, otherwise it's time to contact Customer Support)
If you were to log back in, you'd get a fresh JWT and a new signature would be stored in the Users table. If your S/O were to try to use the site, they would not be authenticated with the their old JWT and would be forced to sign back in.
Trade-off: If we only store the JWS, or a part of it as I suggested, multiple users can't be signed in to the same account at once. How you feel should feel about that really depends on your app.
If I delete a user that has cookie based authorization, the will still have access to the system until he presses the logout button. Or until the cookie expires.
How to fix the situation.
This is a general problem of claims based authentication when removing access for users. Due to how it is designed, the database is not accessed on every request to verify the access. Instead, the cryptographically signed cookie is considered to be the source of truth. So when the access is removed in the database, the cookie is still valid and it is not trivial how to update the user’s cookie in that case. Since it is a cookie that is stored on the client side, you also can’t just log out the user remotely.
But there is a mechanism in ASP.NET Core Identity that enforces the cookie authentication scheme to re-validate an identity on a certain interval. You can configure this like this in your Startup’s ConfigureServices method:
services.Configure<SecurityStampValidatorOptions>(options =>
{
options.ValidationInterval = TimeSpan.FromMinutes(10);
});
This would set the interval to 10 minutes. So every 10 minutes, a user accessing your site with an existing cookie would be validated, and a new cookie would be issued. This process is completely silent and happens behind the scenes.
Depending on how strictly you want to enforce this, you would have to lower this interval further. I would generally recommend you to still leave it at a certain duration. Otherwise you are defeating the purpose of the cached identity.
You should also consider how problematic it really is if a user still has access to your site, and how time critical a user removal would have to be. Depending on your application, it’s also not unlikely that you retrieve the user entity within your critical actions anyway, so this would automatically fail in this case, without you having to deny access by removing the cookie.
I read the documentation of Nancy Forms Authentication. As far as I can tell, the approach recommended there leads to lifelong session IDs:
"The identifier is the token that will be put in the authentication
cookie which will be used to re-establish the identity of the user
that is performing the request, so that you do not need to enter your
credentials for each request."
As far as I understand, that "identifier" is what most people call a session ID.
It is also important to know that the identifier should be treated as
permanent for the user that it was generated for and will be reused
across requests and application sessions.
Is this really the recommended approach? If I understand correctly, this means that session IDs never change and never expire. So the session ID is the equivalent of a password, which
is retransmitted in a cookie with every request
is probably stored in clear-text in the DB, if you follow the docs to the end
I know that I could implement this differently with Nancy, but my point is that such an approach should not be explained in the docs as reference.
So if an attacker ever succeeds in stealing that session ID, e.g. by an XSS attack, he gains lifelong access to the system.
Please correct me and show me the mistake in my thoughts!
The identifier you are referring to isn't a session id, it's an unpredictable user identifier, which is then mapped (if necessary) to the real user identifier in the back end. This is so if someone logs in as user X, and somehow manages to decrypt, re-encrypt and re-sign the cookie they can't just change the user ID to "admin" or something similar and gain admin access (which is how the ASP.Net Oracle attack worked). It's also HttpOnly, so not really capturable via XSS, although technically it could be captured using XST.
Creating and expiring a session (and deleting the auth cookie if necessary) is a different task altogether - how and when you determine if an auth cookie should be accepted, removed, or confirmed with an additional password request is application specific. This is a common pattern now where a site will consider you "logged in" eternally, until you do something "secure", in which case it will ask you to revalidate if you haven't done so recently.
Hope that makes sense.
that "identifier" is what most people call a session ID.
It's not. It's something like UserId. as the documentation states:
We have chosen to use a GUID as the identifier. The reason for this is that using something like the username of id is a potential vulnerability, because, if the cookie encryption was somehow compromised, it would be easy to spoof the identity of another user by guessing their username or id.
They're just using a GUID assigned to the user for more security. Of course, (cookie based) FormsAuthentication has all the disadvantages of cookies. If someone can get access to your auth cookie, they can authenticate themselves as you. But session cookies and forms authentication cookies are completely different things, This answer states the differences pretty clearly.
I am creating a website that lets users run tests and hosts study content, I do not wish for any logged in user to be able to have more than one logical session. So essentially restricting simultaneous usage from multiple browsers or computers logged into the same account by invalidating any requests coming from any non-current/latest session. But I am not too sure on how to best hook into this with asp.net forms authentication.
I am thinking:
Create a db table called ActiveSessions, with rows: UserID, UniqueSessionID. Store this UniqueSessionID in the encrypted AuthCookie on the client.
If a user is not authenticated and logs in via standard login page /account/login etc. Create new UniqueSessionID and store it in the AuthCookie and in the ActiveSessions table respectively, overwriting any existing value. I presume I would have to decrypt and copy contents of the default AuthCookie forms authentication will issue, then create a new AuthCookie using this copied data and also insert my UniqueSessionID into it before returning it to the client.
In the event a request comes in for which the AuthCookie holds a UniqueSessionID that does does not match the one in the database or the record is missing for the respective user, invalidate the session and redirect browser to login page or kill session and issue error for Ajax requests.
Create some kind of scheduled service that cleans up records in the ActiveSessions table based on where User LastActivityDate exceeds some length of time etc.
Ideally I am hoping forms authentication provides some hooks where I can stick this logic in and avoid doing this with attributes over controllers/methods etc.
Also I wish to avoid using session state and its cookie entirely.
I had a very similar requirement. My situation was such that I had to make sure that user ID's were logged in from just one device at a time (Forms Authentication, by the way, not AD). When a user ID tried to log in to another device while still logged in to an existing device, it killed the session on their existing device while allowing them to log-in to the new device. The implementation I created has worked perfectly and so far, have found no flaws in the design. I wrote up a solution on my original post on Stack Overflow:
When the same user ID is trying to log in on multiple devices, how do I kill the session on the other device?
What is the difference between a Session and a Cookie?
What circumstances should each be used?
Sessions
Sessions are stored per-user in memory(or an alternative Session-State) on the server. Sessions use a cookie(session key) to tie the user to the session. This means no "sensitive" data is stored in the cookie on the users machine.
Sessions are generally used to maintain state when you navigate through a website. However, they can also be used to hold commonly accessed objects. Only if the Session-state is set to InProc, if set to another Session-State mode the object must also serializable.
Session["userName"] = "EvilBoy";
if(Session["userName"] != null)
lblUserName.Text = Session["userName"].ToString();
Cookies
Cookies are stored per-user on the users machine. A cookie is usually just a bit of information. Cookies are usually used for simple user settings colours preferences ect. No sensitive information should ever be stored in a cookie.
You can never fully trust that a cookie has not been tampered with by a user or outside source however if security is a big concern and you must use cookies then you can either encrypt your cookies or set them to only be transmitted over SSL. A user can clear his cookies at any time or not allow cookies altogether so you cannot count on them being there just because a user has visited your site in the past.
//add a username Cookie
Response.Cookies["userName"].Value = "EvilBoy";
Response.Cookies["userName"].Expires = DateTime.Now.AddDays(10);
//Can Limit a cookie to a certain Domain
Response.Cookies["userName"].Domain = "Stackoverflow.com";
//request a username cookie
if(Request.Cookies["userName"] != null)
lblUserName.Text = Server.HtmlEncode(Request.Cookies["userName"].Value);
sidenote
It is worth mentioning that ASP.NET also supports cookieless state-management
Cookie is a client side storage of your variables. It stored on client machine by browser physically. It's scope is machine wide. Different users at same machine can read same cookie.
Because of this :
You should not store sensitive data on cookie.
You should not store data that belongs to one user account.
Cookie has no effect on server resources.
Cookie expires at specified date by you.
Session is a server side storage of your variables. Default, it stored on server's memory. But you can configure it to store at SqlServer. It's scope is browser wide. Same user can run two or more browsers and each browser has it's own session.
Because of this :
You can save sensitive data in session.
You should not save everything in session. it's waste of server resources.
After user closes browser, session timeout clears all information. (default is 20 minutes)
A cookie is an identifaction string stored by a server (who has a domain) in the browser of the user who visits the server/domain.
A session is a unit of maybe variables, state, settings while a certain user is accessing a server/domain in a specific time frame. All the session information is in the traditional model stored on the server (!)
Because many concurrent users can visit a server/domain at the same time the server needs to be able to distinguish many different concurrent sessions and always assign the right session to the right user. (And no user may "steal" another uses's session)
This is done through the cookie. The cookie which is stored in the browser and which should in this case be a random combination like s73jsd74df4fdf (so it cannot be guessed) is sent on each request from the browser to the server, and the server can assign and use the correct session for its answers (page views)
The cookie allows the server to recognize the browser/user. The session allows the server to remember information between different page views.
Sessions are not reliant on the user allowing a cookie. They work instead like a token allowing access and passing information while the user has their browser open. The problem with sessions is that when you close your browser you also lose the session. So, if you had a site requiring a login, this couldn't be saved as a session like it could as a cookie, and the user would be forced to re-login every time they visit.
Its possible to have both: a database primary key is hashed and stored in a lookup table: then the hash is stored on the client as a cookie. Once the hash cookie (hahhahaha :) is submitted, its corresponding primary key is looked up, and the rest of the details are associated with it in another table on the server database.
The main difference between cookies and sessions is that cookies are stored in the user's browser, and sessions are not. This difference determines what each is best used for.
A cookie can keep information in the user's browser until deleted. If a person has a login and password, this can be set as a cookie in their browser so they do not have to re-login to your website every time they visit. You can store almost anything in a browser cookie.
Session is a server side object,
which transfer or access data between page call.
Cookies is a object which is client side/client machine which store some text information of browser and server.
There appears to be some confusion regarding what a session cookie is.
Firstly, when we are talking session cookies - it has nothing to do with ASP.Net sessions. Likewise, session cookies have nothing to do with server side processes or caching.
A session cookie is nothing more than a cookie that expires when the browser session expires. To create a session cookie - don't put an expiration date on it. Doing this stores the cookie in memory and is disposed of when the browser is disposed.