I have an asp.net MVC application that was exhibiting some strange authentication timeout behavior. We had the timeout on the forms authentication tag in the web.config set to two days.
<forms loginUrl="~/Login/Index" timeout="2880" />
What we noticed though was the application was timing out around the 20 min mark. So, after researching the issue and trying various settings we decided to add code to the global.asax in the Applicatoni_AuthenticateRequest method that reads the ticket from the cookie, and updates the expiration .
Sub Application_AuthenticateRequest(sender As Object, e As EventArgs)
Dim authCookie As HttpCookie = Context.Request.Cookies(FormsAuthentication.FormsCookieName)
If (IsNothing(authCookie) OrElse authCookie.Value = "") Then
Return
End If
Dim authTicket As FormsAuthenticationTicket
Try
authTicket = RefreshLoginCookie()
Catch
Return
End Try
'string[] roles = authTicket.UserData.Split(';');
'if (Context.User != null) Context.User = new GenericPrincipal(Context.User.Identity, roles);
End Sub
Function RefreshLoginCookie() As FormsAuthenticationTicket
Dim authCookie As HttpCookie = HttpContext.Current.Request.Cookies(FormsAuthentication.FormsCookieName)
If (IsNothing(authCookie) OrElse authCookie.Value = "") Then
Return Nothing
End If
Dim oldTicket As FormsAuthenticationTicket = FormsAuthentication.Decrypt(authCookie.Value)
Dim expiryDate As Date = DateTime.Now.Add(FormsAuthentication.Timeout)
HttpContext.Current.Response.Cookies.Remove(FormsAuthentication.FormsCookieName)
Dim newTicket As FormsAuthenticationTicket = New FormsAuthenticationTicket(oldTicket.Version, oldTicket.Name, oldTicket.IssueDate, expiryDate, oldTicket.IsPersistent, oldTicket.UserData, oldTicket.CookiePath)
authCookie.Value = FormsAuthentication.Encrypt(newTicket)
authCookie.Expires = expiryDate
HttpContext.Current.Response.Cookies.Add(authCookie)
Return newTicket
End Function
Testing this in visual studio 2012 debugger everything worked beautifully, but once we deployed it to our IIS 7 environment the timeout issue returned.
To aid in my debugging I set the cookie expiration to be the same as the ticket's expiration so that I could easily tell what the ticket was being set to.
When the timeout occurs I see that the expiration should have occurred on 11/16, but that's not what's happening :
Here is the cookie information (at least the relevant information)
Name Expiration
.ASPXAUTH 11/16/2014, 9:00:47
putting logging in I see that the expiration update succeeds on the next request, but I still am getting sent to the login.
I'm stumped. I know it must be some setting in IIS 7 that is causing this because as I stated, it works fine in the debugger in visual studio (which is IIS 8 Express) so there must be something somewhere overriding this functionality.
I think it may be something specific to MVC though, because I'm using this same functionality on another site and am not seeing the same problem.
UPDATE
Forgot to mention, I'm also NOT seeing an authentication ticket timeout record in the event viewer.
Ok, so I finally figured out the answer to this problem. The Application Pool was hitting its Idle Timeout setting of 20 minutes. So, after 20 minutes of inactivity in the application, the app pool was recycled. Thus, when the user would eventually continue, the pool would reinitialize, and the current authentication cookie was no longer valid.
To modify this setting, go into the advanced settings on your application pool in IIS. Setting it to 0 will disable the idle timeout, or you can set it to whatever value in minutes that you determine appropriate.
Related
I've been reading a lot about session fixation attacks and the most popular solutions I've come across are changing the SessionID when user logs in and creating an additional cookie using a GUID to verify the user "belongs" to the SessionID.
My question is this: Isn't it enough to just delete the SessionID cookie (ASP.NET_SessionID) to ensure a new SessionID is generated?
In MVC 5, when the user logs in an additional encrypted user claims cookies is created (AspNet.ApplicationCookie) which Identity uses to authenticate the user upon each request. The additional "GUID cookie" seems unnecessary.
I’m originally a .NET desktop application developer writing my first MVC app and the learning curve has been a bit steep… although refreshingly enjoyable.
Thanks for any help.
Let me try to explain the issue and the solution by using comparisons between desktop and web apps (both in .Net)
When you start your desktop app, the first thing the app shows is a login screen, after which your access to the UI is granted. Now, each time the app's exe is started, it writes the "RunID" to a text file and shows the login screen. The RunID is how the rest of your usage of the app is going to be tracked/correlated.
Assume for a second that the file was on C:\RunID.txt.
An attacker (hacker) can start the exe (without logging in) on Machine1 and copy the contents of C:\RunID.txt to Machine2. Now as soon as you log in on Machine1, the RunID token from Machine1 will also work on Machine2, this is called session fixation.
The ideal way to fix it is to ABANDON the pre-authentication token, and issue a NEW Post-Authentication token. So, you would get a new Token after authentication (or in your case, an additional GUID) which will NOT EXIST on Machine2 and hence provide a level of security in addition to the RunID random token (Session ID)
Let me know if you'd like further explaination, but that is why even in MVC, you should abandon the previous session and create a new session post-auth to avoid session fixation, as a compensating control, you can add a GUID cookie too correspond with the Session ID cookie.
You can do this to avoid that situation:
SessionIDManager Manager = new SessionIDManager();
string NewID = Manager.CreateSessionID(Context);
string OldID = Context.Session.SessionID;
bool redirected = false;
bool IsAdded = false;
Manager.SaveSessionID(Context, NewID, out redirected, out IsAdded);
Response.Write("Old SessionId Is : " + OldID);
if (IsAdded)
{
Response.Write("<br/> New Session ID Is : " + NewID);
}
else
{
Response.Write("<br/> Session Id did not saved : ");
}
Support link:
Link
I had a Posting on a blog about Sessions AND Cookies. Here are details
Sessions
Sessions are More Secure
Sessions are on the server
Cookies
Cookies are On client side
Less Secure
Once it is disable on browser the difficult to use.
On the basis of above argument i used sessions in Login system to keep UserId,UserName & roleName
Now on the the basis of roleName i will decide either this is Admin to enter to administrator section or not.
I have used this Code in Model in MVC
public bool LoginMe()
{
Int64 Error;
//create db
Database db = DatabaseFactory.CreateDatabase("DBContext");
DbCommand dbCommand = db.GetStoredProcCommand("ValidateUser");
db.AddInParameter(dbCommand, "#Username", DbType.String, this.UserName);
db.AddInParameter(dbCommand, "#Password", DbType.String, EncryptPassword(this.Password));
db.AddOutParameter(dbCommand, "#Error", DbType.Int64, 10);
DataSet dsResult = db.ExecuteDataSet(dbCommand);
Error = Convert.ToInt64(db.GetParameterValue(dbCommand, "#Error"));
if (Error == 1100)
{
try
{
var query = (from o in dsResult.Tables[0].AsEnumerable()
select new AllUser
{
UserId = o.Field<int>("UserId"),
UserName = o.Field<string>("UserName"),
roleName = o.Field<string>("roleName"),
}).Single(); // this will raise an exception if there isn't just one record returned
Session["UserId"] = query.UserId;
Session["UserName"] = query.UserName;
Session["roleName"] = query.roleName;
return true;
}
catch {
// do nothing and let method return false as something has gone wrong.
// add logging here if you are using it to show there has been a problem
}
}
return false;
}
I used it in View like #Session["UserId"]
Now an expert comment on this like
If you aren't using https and securing the session cookie then this might make it easy to hack your site, although that's the same for any session based site (nearly all of them)
It might be nice to add some check so that if you remove a user's rights, the session variables are deleted the next time that user requests something from the server,
otherwise they could carry on using the site even though their account it banned.You'd have to decide if this is likely and then how you want to do this (using an authorization filter maybe.)
Above comments confused me.Can any body make it clear?What is the best way to keep these information?
Session state uses client tickets to identify the server-side session, it may be susceptible to session ID spoofing and injection attacks.
So, to hack session values one would require hacking the remote-server.
And yes, for highly secure application(such as online banking) use https.
http://msdn.microsoft.com/en-us/magazine/cc163730.aspx#S9
Secure sockets layer (SSL) should be used to prevent network-level sniffing of session IDs, authentication tickets, application cookies, and other request/response information.
Can session value be hacked?
Use HTTPS if you application handles sensitive information(credit-card number,account num,passwords).
Store the User object (model with userId,username,role) in the session than separate attributes
Set setHttpOnly attribute for SESSION_ID.
It might be costly to refresh the User object stored in session before invoking every operation to reflect the current rights stored in database.
I have created a new ASP.NET MVC application with .NET 4.5. I have successfully set up authentication with an STS. The authentication flow is working fine and I am able to get the ClaimsIdentity, containing the desired claims, on Thread.CurrentPrincipal.
Now I need the bootstrap token to secure the calls to my service layer. I have set the saveBootstrapContext to true on the identityConfiguration element.
<system.identityModel>
<identityConfiguration saveBootstrapContext="true">
However, the BootstrapContext property on the ClaimsIdentity is always null.
var identity = Thread.CurrentPrincipal.Identity as ClaimsIdentity;
var context = identity.BootstrapContext; // context is always null
Am I missing anything here? This was supposed to be straightforward :(
Solved it by these:
<system.identityModel>
<identityConfiguration saveBootstrapContext="true" />
</system.identityModel>
Also need to set TokenValidationParameters.SaveSigninToken, which is distinct from JwtBearerOptions.SaveTokens:
app.UseWindowsAzureActiveDirectoryBearerAuthentication(
new WindowsAzureActiveDirectoryBearerAuthenticationOptions {
Tenant = ConfigurationManager.AppSettings["ida:Tenant"],
TokenValidationParameters = new TokenValidationParameters {
SaveSigninToken = true,
ValidAudience = ConfigurationManager.AppSettings["ida:Audience"]
}
}
);
I ran into this problem when hosting in IIS Express. It turns out that the issue was my browser - I had not closed all of my browser windows or cleared cookies, so the SessionSecurityToken was not being recreated with the new setting, even though the server had been restarted (the existing FedAuth cookie was still being sent from the browser).
Once I forced a re-authentication by closing all browser windows, restarting the browser and performing my request again, the BootstrapContext was present.
If you're using a message handler to manually validate the token using the JwtSecurityTokenHandler to extract a claims principal and attach that to the current Thread, as described here in Using the JWT handler for Implementing “Poor Man”’s Delegation/ActAs, when you're validating the token using JwtSecurityTokenHandler.ValidateToken(), one of the settings on TokenValidationParameters is
SaveBootstrapContext, setting that true does the trick.
I'm using Microsoft.AspNetCore.Authentication.OpenIdConnect, Version=5.0.4.0, and the setting is this:
.AddOpenIdConnect(o =>
{
// . . .
o.TokenValidationParameters.SaveSigninToken = true;
})
I have an MVC site running on mono 2.10.8.1 on apache2 undo ubuntu. I have a service that checks credentials and set cookies if all good. In windows i have no problem and got only one cookie: .ASPXAUTH. However when i deploy to linux server, it sets two cookies ASP.NET_SessionId and .MONOAUTH.
My question is why it is two cookies in linux and one in windows, and how can i get rid of ASP.NET_SessionId cookie?
I'm setting cookie like this:
Response.AppendCookie(BuildAuthenticationCookie(login, data));
public static HttpCookie BuildAuthenticationCookie(string login, string ticketData)
{
var authTicket = new FormsAuthenticationTicket(2, login, DateTime.Now,
DateTime.Now.AddMinutes(500), false, ticketData);
var authCookie = new HttpCookie( FormsAuthentication.FormsCookieName, FormsAuthentication.Encrypt(authTicket))
{
HttpOnly = true,
Secure = SiteSettings.CookiesSslOnly,
Expires = DateTime.Now.AddMinutes(SiteSettings.AuthCookieExpirationTime)
};
return authCookie;
}
I think there is nothing wrong with Mono. In fact, the two cookies you mention - ASP.NET_SessionId and .MONOAUTH are the Session and Forms Authorization cookies, which really are different.
To get rid of the ASP.NET_SessionId, you can either not use Sessions at all or have cookieless sessions (<sessionSate> on MSDN).
Note: You should also get two cookies on Windows. If not, it probably means that you didn't hit any MVC Actions that set some value in the SessionState. If you manage to find such an Action, you will get two cookies on Windows as well.
i have a problem with session in my webapplication (asp.net mvc rc2). the application works fine on asp.net mvc rc1.
i use the follow code for session timeout handling:
if (cnt.Current.Session != null)
{
if (cnt.Current.Session.IsNewSession)
{
string cookie = cnt.Current.Request.Headers["Cookie"];
if ((null != cookie && (cookie.IndexOf("ASP.NET_SessionId") >= 0))
{
return true;
}
}
}
when i becomes a session timeout, use the FormsAuthentication.SignOut(); method for user logout an redirect to the loginpage.
after the new login is cnt.Current.Session.IsNewSession always true!! (BUG?)
on rc1 works fine.
MVC 2 RC1 had a bug which incorrectly created a new session for every user. The correct behavior (which MVC 2 RC2 exhibits) is that session generation is deferred until the first time something is stored in the user's session.
If you never store anything in Session, then it will never get saved, and IsNewSession will always return true. Please be sure that you're actually putting something in Session if you intend to use it.
On a related note, if you're just using this to control when a user logs out, why not just shorten the FormsAuth cookie expiration? The default timeout is 2880 minutes (2 days); you can change this in the site's root Web.config. This way you don't have to mess with Session at all.