I'm using the Membership API for my login system and a wierd thing has been bothering me. I allow user to choose whether to "Remember Me", which means whether to create a persistent cookie with one month expiration period.
In the Web.config, I wrote:
<authentication mode="Forms">
<forms timeout="60" />
</authentication>
This is supposed to be a default session cookie of 60 minutes.
In the code behind of the login page:
if(Membership.ValidateUser(UsernameTextBox.Text, PasswordTextBox.Text))
{
authCookie = FormsAuthentication.GetAuthCookie(UsernameTextBox.Text, RememberMeCheckBox.Checked);
if(RememberMeCheckBox.Checked)
authCookie.Expires = DateTime.Now.AddMonths(1);
Response.Cookies.Add(authCookie);
Response.Redirect(FormsAuthentication.GetRedirectUrl(UsernameTextBox.Text, RememberMeCheckBox.Checked));
}
The result however is strange. I seem to have created a persistent cookie of 60 minutes! How is this possible?
You are setting the cookie expiration time to be be 1 month, but the authentication ticket that it contains has not been modified. It has inherited the default value of 60 minutes from your web config.
You likely want to synchronize cookie expiration with the authentication ticket expiration, or alternatively set the cookie to have a very long expiry date.
What you need to do is
create a FormsAuthenticationTicket instance manually and set the
Expiration property of the instance.
Use FormsAuthentication.Encrypt() to encrypt the ticket
Add a cookie to the Response.Cookies collection containing the ticket, manually. (rather then using get/setAuthCookie(), which uses
the web.config settings).
Some example code is in the documentation for FormsAuthentication.Encrypt().
Related
First off, this is not a problem with the ASP.NET session not expiring, we clear, abandon, and delete every cookie on logout.
This is about FormsAuthentication.SignOut() not expiring the ticket when called and allowing someone who copies the content of the cookie to manually create the cookie somewhere else and still be able to acces everything that is meant to now be blocked off after the logout.
Here is the gist of our logout method:
HttpContext.Current.User = null;
HttpContext.Current.Response.Cache.SetCacheability(HttpCacheability.NoCache);
HttpContext.Current.Session.Clear();
HttpContext.Current.Session.Abandon();
HttpContext.Current.Session.RemoveAll();
HttpContext.Current.Response.Cookies.Add(new HttpCookie("ASP.NET_SessionId", ""));
FormsAuthentication.SignOut();
FormsAuthentication.RedirectToLoginPage();
We also let ASP manage the creation of the ticket and the authentication via our Web.config and whatever else manages FormsAuthentication. Here is what is in the config:
<authentication mode="Forms">
<forms name="COOKIENAME" loginUrl="~/PAGE_THAT_REDIRECTS_TO_LOGIN.aspx" defaultUrl="~/PAGE_THAT_REDIRECTS_TO_LOGIN_OR_PROPER_PAGE_IF_LOGGED_IN.aspx" cookieless="UseCookies" timeout="60" />
</authentication>
Now, why is this an issue? simple, it's a security concern as if someone gets the cookie and keeps it alive, they can access whatever the user matching the cookie can, even though the user has been disconnected.
Is there is a proper way to force the FormsAuthenticationTicket to expire?
I tried decrypting it, but everything is readonly, and I also tried to create a new expired ticket and encrypting it, but it doesn't overwrite the old one.
Thanks
Versions: .NET 4.5.1, ASP.NET (not Core)
The basic problem is with Microsoft .net Core cookie Managemnt, it does not handle the lifetime of cookies correctly.
I had face this issue several times, and mostly with .Net core now.
To solve this issue we need to override their cookie management class, and implement ITicketStore interface.
https://github.com/aspnet/Security/blob/master/src/Microsoft.AspNetCore.Authentication.Cookies/CookieAuthenticationOptions.cs#L136
Below article can help you with detail implementation.
https://mikerussellnz.github.io/.NET-Core-Auth-Ticket-Redis/
I hope it helps.
We have Session hijacking and Session fixation problem with our asp.net application.
We have implemented SSL also.
1.. I have added below code in web.config file.
<----
<httpCookies httpOnlyCookies="true" requireSSL="true" />
<forms loginUrl="Homepage.aspx"
protection="All"
timeout="20"
name=".ASPXAUTH"
path="/"
requireSSL="true"
slidingExpiration="true"
/>
--->
2... Encrypting formsathuntication ticket and adding to the cookie after user is athunticated.
<---
FormsAuthenticationTicket tkt;
string cookiestr;
HttpCookie ck;
tkt = new FormsAuthenticationTicket(1, uname, DateTime.Now, DateTime.Now.AddMinutes(20),false, "your custom data");
cookiestr = FormsAuthentication.Encrypt(tkt);
ck = new HttpCookie(FormsAuthentication.FormsCookieName, cookiestr);
ck.Path = FormsAuthentication.FormsCookiePath;
Response.Cookies.Add(ck);
-->
3.. I'm removing session variables and passing null value to ASP.NET_SessionID on logout page and Error page.
SessionHandler.EndSession();
Session.RemoveAll();
Session.Abandon();
Response.Cookies.Add(new HttpCookie("ASP.NET_SessionId", ""));
if (Request.Cookies["ASP.NET_SessionId"] != null)
{
Response.Cookies["ASP.NET_SessionId"].Value = string.Empty;
Response.Cookies["ASP.NET_SessionId"].Expires = DateTime.Now.AddMonths(-20);
}
if (Request.Cookies["AuthToken"] != null)
{
Response.Cookies["AuthToken"].Value = string.Empty;
Response.Cookies["AuthToken"].Expires = DateTime.Now.AddMonths(-20);
}
HttpCookie authcookie = Request.Cookies[FormsAuthentication.FormsCookieName];
authcookie.Expires = DateTime.Now.AddDays(-1D);
Response.Cookies.Add(authcookie);
FormsAuthentication.SignOut();
still problem is not solved...
Is your problem session hijacking, or authentication hijacking?
Are you trusting session values without validating identity? (Note that session and authentication are not intrinsically linked in ASP.NET).
If you've implemented SSL, why is your session cookie still set to requireSSL="false"?
Research best-practice, and see for yourself where you've gone wrong. For example - http://www.troyhunt.com/2010/07/owasp-top-10-for-net-developers-part-3.html
To elaborate on point 2.
There are two cookies in use here, one is for Session, the other for FormsAuthentication.
The FormsAuth cookie identifies the user, and all reasonable steps need to be taken to keep this one secure. Typically, requiring SSL is a good step (as you've applied in the edit of your example). The Session cookie though, often doesn't come under as close scrutiny for developers, but depending on how you're using it can be just as sensitive. A session fixation steals the session, not the authentication.
An example:
UserA logs in, they are an admin user. They receive a FormsAuth cookie stating "This is UserA", they might also get a session cookie stating "This User Is Admin".
UserB gets a copy of the session cookie belonging to UserA (they might do this via interception, or even just by being on the same machine after UserA logs out if the session cookie isn't cleared).
UserB logs in, they are a "read-only" user (not admin). They receive a FormsAuth cookie stating "This is UserB", they then inject the session cookie stolen at step 2. Meaning they have a FormsAuth cookie stating "This is UserB", and a Session cookie stating "This User Is Admin".
Presto, UserB is now admin.
The problem here (as it relates to point 2 of my original list of concerns), is that the server didn't verify the identity of the user in relation to its session. You can do your best to try and link the Session and Forms authentication tickets together somehow, and definitely make certain you're encrypting (SSL). OR, you can stop storing sensitive data in the session (or at least reduce it). When it comes to my "This User Is Admin" example above, the better implementation is to use the ASP.NET Role and Profile providers in conjunction with the Membership provider. The three of them go hand in hand, and there's a lot of examples out there on how to use them to your advantage.
This is only one possible line of investigation though and as #JohnFx rightly pointed out, you really need a focused question here before you can expect a good answer. When it comes to security implementation, it's important to understand the concepts involved, instead of just throwing example code at the issue. Your example code provided thus far looks suspiciously similar to a CodeProject article discussing session fixation, but do you understand what it's trying to accomplish? Do you know if it even applies to the problem you're experiencing?
My session renews every 20 minutes. I've set timeout to 300 minutes but still it renews probably because Application Pool recycles.
I am storing UserId which is Guid in Session which returns null. Problem is when I use Membership using
Membership.GetUser().ProviderUserKey
it works fine. But obviously it makes a database call. How can I prevent this problem from happening? Why does Membership.GetUser().ProviderUserKey succeeds whereas Session doesn't?
In order to complete Jan's and Neil's answers, you should look at your web.config and set both timeouts (sessionState and authentication)
<sessionState timeout="300"/>
Sessionstate timeout specifies the number of minutes a session can be idle before it is abandoned. The default is 20.
<authentication mode="Forms">
<forms loginUrl="Login.aspx" timeout="300" />
</authentication>
Forms timeout is used to specify a limited lifetime for the forms authentication session. The default value is 30 minutes. If a persistent forms authentication cookie is issued, the timeout attribute is also used to set the lifetime of the persistent cookie.
Your session may still be alive (if you set it to 300 minutes) but the ASP.NET membership could be expiring?
Have you increased the authentication timeout too?
<authentication mode="Forms">
<forms loginUrl="Login/" timeout="180"/>
</authentication>
You are mixing authentication and session. These are two completely different concepts.
GetUser() return the currently authenticated user form your MemberShipProvider.
Session and authentication have different timeouts - so its valid that your session times out but the user is still authenticated.
In my web app I'm using some session variables, which are set when I login:
e.g. Session("user_id") = reader("user_id")
I use this through my app.
When the session variable times out, this throws errors mainly when connecting to the database as session("user_id") is required for some queries.
How can I set my session variables so that once they are timed out to go to the login page or how can at least increase the length of time the are available?
I'm guessing you're using Forms Authentication. The trick here is to ensure that your Forms Authentication expires before the session does.
I wrote about this in this answer here:
How to redirect to LogIn page when Session is expired (ASP.NET 3.5 FormsAuthen)
For example:
Configure your Forms Authentication - this sets the timeout to 60 minutes:
<authentication mode="Forms">
<forms defaultUrl="~/Default.aspx"
loginUrl="~/Login.aspx"
slidingExpiration="true"
timeout="60" />
</authentication>
Extend Session expiry to a longer time:
<sessionState
mode="InProc"
cookieless="false"
timeout="70"/>
In your Login.aspx code behind you could also do a Session.Clear(); to remove stale session data before assigning session values.
In the past I've used a base page or master page on every page (making an exception for the login page) that reads a session token to see if a user is logged in currently.
If it ever reads a null it saves the current url and redirects to the login page.
After logging in it reads the saved url and redirects the user back to the requested page.
Increasing the session timeout value is a setting in IIS.
How can I set my session variables so that once they are timed out to go to the login page
Check if they are = null do a Response.Redirect("Home.aspx");
or how can at least increase the
length of time the are available?
Its in the web.config within the sessionState element
I think a lot of people wrap their session calls to provide a "lazy load" pattern. Something like this:
class SessionHelper
{
public static string GetUserId()
{
string userId = (string)System.Web.HttpContext.Current.Session["UserId"];
if( userId == null )
{
userId = reader("UserId");
System.Web.HttpContext.Current.Session["UserId"] = userId;
}
return userId;
}
}
In an ASP.NET MVC2 app, we have the standard login action...
if (ValidateUser(model.Email, model.Password)
{
FormsAuthentication.SetAuthCookie(model.Email, model.RememberMe);
...
where the second parameter to SetAuthCookie is createPersistentCookie with the following documentation:
createPersistentCookie
Type: System.Boolean
true to create a persistent cookie
(one that is saved across browser sessions); otherwise, false.
We would like to have the persistent cookie expire after 2 weeks (i.e., a user could return to the site within 2 weeks and not be required to re-authenticate. After that time they would be asked to login again).
How do we set the expiration for the persistent cookie?
Can you not do this?
<system.web>
<authentication mode="Forms">
<forms timeout="20160"/>
</authentication>
</system.web>
The timeout is in minutes.
This timeout value is irrespective of whether or not you are creating a persistent cookie. It simply says that if you don't explicitly terminate the cookie (FormsAuthentication.SignOut), it will automatically expire after the given time period.
In other words, if you do:
FormsAuthentication.SetAuthCookie(someMembershipName, false);
Will result in the cookie expiring when:
The user closes the browser, or
The timeout is reached.
As opposed to if you do:
FormsAuthentication.SetAuthCookie(someMembershipName, true);
Will result in the cookie only expiring when the timeout is reached.
HTH
EDIT:
Take from MSDN:
the timeout attribute is described as follows:
Specifies the time, in integer
minutes, after which the cookie
expires. If the SlidingExpiration
attribute is true, the timeout
attribute is a sliding value, expiring
at the specified number of minutes
after the time that the last request
was received. To prevent compromised
performance, and to avoid multiple
browser warnings for users who have
cookie warnings turned on, the cookie
is updated when more than half of the
specified time has elapsed. This might
cause a loss of precision. The default
is "30" (30 minutes).
Note Under ASP.NET V1.1 persistent
cookies do not time out, regardless of
the setting of the timeout attribute.
However, as of ASP.NET V2.0,
persistent cookies do time out
according to the timeout attribute.
In other words, this expiration setting handles the Forms Authentication cookie only.
The Forms Authentication cookie is a client-side cookie, it has nothing to do with other server-side session you may have (ie a Shopping Cart).
That Session is expired with the following setting:
<sessionstate
mode="inproc"
cookieless="false"
timeout="20"