ASP.NET MVC RC2 - Session Handling Bug? - asp.net-mvc

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.

Related

OWIN authentication middleware: logging off

OWIN beginner here. Please be patient...
I'm trying to build an OWIN authentication middleware which uses form posts to communicate with my external authentication provider. I've had some success with getting the authentication bits working. In other words, I can:
communicate with the remote provider through form post;
process the response returned by the remove provider
If everything is ok, I'm able to signal the default authentication provider
THis in turn gets picked up by the cookie middleware which ends up generating the authentication cookie
So far, so good. Now, what I'd like to know is how to handle a log off request. Currently, the controller will simply get the default authentication manager from the owin context and call its SingOut method. This does in fact end my current session (by removing the cookie), but it really does nothing to the existing "external" session.
So, here are my questions:
1. Is the authentication middleware also responsible for performing log off requests?
2. If that is the case, then can someone point me to some docs/examples of how it's done? I've found some links online which describe the logging in part, but haven't found anything about the log off process...
Thanks.
Luis
After some digging, I've managed to get everything working. I'll write a few tips that might help someone with similar problems in the future...
Regarding the first question, the answer is yes, you can delegate the logoff to the middleware. If you decide to do that, then your middleware handler should override the ApplyResponseGrantAsync method and check if there's a current revoke request. Here's some code that helps to illustrate the principle:
protected override async Task ApplyResponseGrantAsync() {
var revoke = Helper.LookupSignOut(Options.AuthenticationType,
Options.AuthenticationMode);
var shouldEndExternalSession = revoke != null;
if (!shouldEndExternalSession) {
return;
}
//more code here...
}
After checking if there's a revoke request, and if your external authentication provider is able to end the response through a redirect, then you can simply call the Response.Redirect method (don't forget to check for the existance of redirect - ex.: if you're using asp.net identity and MVC's automatically generated code, then the sign out will redirect you to the home page of your site).
In my scenario, things were a little more complicated because communication with my authentication provider was based of form posts (SAML2 messages with HTTP Post binding). I've started by trying to use Response.Write to inject the HTML with the autopostback form into the output buffer:
protected override async Task ApplyResponseGrantAsync() {
//previous code + setup removed
var htmlForm = BuildAndSubmitFormWithLogoutData(url,
Options.UrlInicioSessaoAutenticacaoGov);
Response.StatusCode = 200;
Response.ContentType = "text/html";
await Response.WriteAsync(htmlForm);
}
Unfortunately, it simply didn't work out. Not sure on why, but the browser insisted in redirecting the page to the URL defined by the Logoff's controller method (which was redirecting the page to its home page or '/'). I've even tried to remove the location HTTP header from within the ApplyResponseGrantAsync method, but it still ended up redirecting the user to the home page (instead of loading the predefined HTML I was writing).
I've ended up changing the redirect so that it gets handled by my middleware. Here's the final code I've ended up with in the ApplyResponseGrant method:
protected override async Task ApplyResponseGrantAsync() {
//previous code + setup removed
//setup urls for callbabk and correlation ids
var url = ...; //internal cb url that gets handled by this middleware
Response.Redirect(url);
}
This redirect forced me to change the InvokeAsync implementation so that it is now responsible for:
Checking for a new authentication session
Checking for the end of an existing authentication session (handle the logoff response from the external provider)
Checking if it should generate a new form post html message that ends the current session controlled by the external provider
Here's some pseudo code that tries to illustrate this:
public override async Task<bool> InvokeAsync() {
if (Options.InternalUrlForNewSession.HasValue &&
Options.InternalUrlForNewSession == Request.Path) {
return await HandleLoginReply(); /login response
}
if (Options.InternalUrlExternalSessionEnded.HasValue &&
Options.InternalUrlExternalSessionEnded == Request.Path) {
return await HandleLogoffReply();//logoff response
}
if (Options.InternalUrlForEndingSession.HasValue &&
Options.InternalUrlForEndingSession == Request.Path) {
return await HandleStartLogoutRequest(); //start logoff request
}
return false;
}
Yes, in the end, I've ended with an extra request, which IMO shouldn't be needed. Again, I might have missed something. If someone manages to get the ApplyResponseGrantAsync to return the auto submit post (instead of the redirect), please let me know how.
Thanks.

Are Session Fixation Attacks in MVC 5 still an issue

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

MVC Authentication Timeout Prior to ticket expiration

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.

Single authentication pipeline for webapi, mvc and signalr supporting basic and forms

My current services are using MVC to render forms, WebApi to move my viewModels back and forth and signalR for push notifications etc.
If the users are browsing the website they will be using forms auth, but we're introducing some mobile apps and I would like to be able to consume webapi and signalr from the mobile apps using basic auth, without having to maintain two separate sets of controllers.
I have two IPrincipals, a SessionPrincipal and a BasicPrincipal (where Session Principal inherits BasicPrincipal and has some additional contextual data). The idea is that some controllers will require to be on the website (SessionPrincipal), but everything else can be accessed by both web and mobile users (Basic Principal). Some won't require any authorisation at all, so can't just deny the request.
My current approach does the following steps to achieve this (some code omitted for brevity)
Global.asax Application_AuthenticateRequest
var cultureCookie = Request.Cookies["Culture"];
// Set culture ...
var authHeader = Request.Headers["Authorization"];
if (authHeader != null && authHeader.StartsWith("Basic"))
{
//Check Username / Password. If okay...
HttpContext.Current.User = new BasicAuthPrincipal(user);
}
else
{
var authCookie = Request.Cookies[FormsAuthentication.FormsCookieName];
if (authCookie != null)
{
// Try and resolve Session from encrypted forms auth data. If okay...
HttpContext.Current.User = new SessionAuthPrincipal(Id, User, Agent);
}
}
Individual Authorize Filters (SessionMVC, SessionApi, BasicApi) that basically boil down to:
return HttpContext.Current.User as SessionPrincipal != null;
// Or
return HttpContext.Current.User as BasicPrincipal != null;
So if they were successfully set in global.asax then proceed to the controller.
Now, I have a working implementation of this, so why am I asking for help?
I'm not sure of certain fringe scenarios that may upset this. Am I asking for trouble for implementing it this way?
I read about HttpContext not being thread safe, but Application_AuthenticateRequest should run before everything else and no further changes are made to that data so I think it should be okay.

Keeping User Information in Session in MVC is not Secure

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.

Resources