asp_sessionid cookie default path to be made application specific - c#

I have a website in which I had to make the default path which is usually visible as "/" for asp.netSessionId cookie and .ASPXAUTH cookie to application specific for security reasons and after lot of RND i was able to do that but I have a confusion and a problem now described as follows:
The confusion is: In order to make the cookie path application specific I wrote a method say "setcookiepath" to do so and called that method from global file's session_start but that did not work that as the path either used to get reset sometime after certain clicks or more than 1 cookie got generated one with default path and other with app specific path. So I went ahead and called my method 1 by 1 from other events in global.asax file and finally ended up calling from each one, still it worked only sometimes. Then I abandoned session in my Session_Start only the first time when Application is called by using a flag that i set in application_start and reset after session abandons in session_start. This time it worked perfectly fine. Now, i tried removing unneccesary calls from setcookiepath and tested this time it is working with single method call in any of the events and even after removing the logic of abandoning session it is working now.I am confused what is the right approach?
The problem is: Although now my code is working for the website but there is an Admin module which is called from a sub folder within that website. For this Admin sub module my code is not working i.e. the problem I am facing is. I am able to login to Admin module the first time I hit the url and 2 cookies get generated for ASP.NetSessionId one with default path and the other has application specific path. This works as long as my browser is open, when I close the browser and try to relogin, now my previous session cookies are removed and I keep getting redirected to login page because the user session gets invalidated in between somewhere when I am redirecting from login page to home page for Admin module, this issue is not occuring in main website module. Also, after removing the path reset logic from global.asax.cs file admin module session works fine between login and home page.
Please help.
Code for path set is :
public void setCookieApp()
{
if (HttpContext.Current != null)
{
/// only apply session cookie persistence to requests requiring session information
if (HttpContext.Current.Handler is IRequiresSessionState || HttpContext.Current.Handler is IReadOnlySessionState)
{
SessionStateSection sessionState = (SessionStateSection)ConfigurationManager.GetSection("system.web/sessionState");
string cookieName = sessionState != null && !string.IsNullOrEmpty(sessionState.CookieName) ? sessionState.CookieName : "ASP.NET_SessionId";
string cookieName1 = sessionState != null && !string.IsNullOrEmpty(sessionState.CookieName) ? sessionState.CookieName : ".ASPXAUTH";
//System.TimeSpan timeout = sessionState != null ? sessionState.Timeout : TimeSpan.FromMinutes(20);
/// Ensure ASP.NET Session Cookies are accessible throughout the subdomains.
if (HttpContext.Current.Request.Cookies[cookieName] != null)
{
//Response.Cookies[cookieName].Value = Session.SessionID;
HttpContext.Current.Response.Cookies[cookieName].Path = HttpContext.Current.Request.ApplicationPath;
//Response.Cookies[cookieName].Expires = DateTime.Now.Add(timeout);
}
if (HttpContext.Current.Request.Cookies[cookieName1] != null)
{
//Response.Cookies[cookieName].Value = Session.SessionID;
HttpContext.Current.Response.Cookies[cookieName1].Path = HttpContext.Current.Request.ApplicationPath;
//Response.Cookies[cookieName].Expires = DateTime.Now.Add(timeout);
}
}
foreach (string k in HttpContext.Current.Request.Cookies.AllKeys)
{
if (k.ToString().Contains("ASP.NET_SessionId"))
{
HttpContext.Current.Response.Cookies["ASP.NET_SessionId"].Path = HttpContext.Current.Request.ApplicationPath;
}
else if (k.ToString().Contains(".ASPXAUTH"))
{
HttpContext.Current.Response.Cookies[".ASPXAUTH"].Path = HttpContext.Current.Request.ApplicationPath;
}
}
}
}

Related

Does anyone else have issues losing session variables in Firefox but not in Chrome or IE?

We are experiencing some issues with returning variables stored in session, which in turn is causing our controller authorization code to redirect some users back to the login page. The users only seem to be having this issue with the Firefox browser and the application works normally for them when using Chrome or Edge. It's almost as if their session is being dropped while navigating through the site (mostly immediately after login while being redirected to the home page, however sometimes the user can get through few links before being kicked out).The issue also only happens after if is running in our test or production sites, reproducing the issue locally troublesome. I was wondering if anyone else has experience this type of issue before and if they were able to find a work around or resolve it. Our sessionstate is set to InProc.
Based on other threads I have read on here I have tried the following so far:
Ensured that Session_Start is in Global.asax, Tried setting a "dummy" session variable in the Session_Start method "Session["Init"] = 1", added this to our web.config file under the section
, as well as some other solutions I have found that turned out not to resolve our issue.
This is the section of code in question.
public class AuthorizeSessionAttribute : AuthorizeAttribute, IAuthorizationFilter
{
public override void OnAuthorization(AuthorizationContext filterContext)
{
HttpSessionStateBase session = filterContext.HttpContext.Session;
Controller controller = filterContext.Controller as Controller;
if (controller != null && session != null && session["CurrentRole"] == null)
{
controller.HttpContext.Response.Redirect("~/Account/Login");
}
base.OnAuthorization(filterContext);
}
}
I just can't see any obvious reason as to why this would only happen in Firefox but work correctly in Chrome or IE. Any input on this would be appreciated. Thanks.

Sharepoint 2013 - when user login give a new session token and expire all previous tokens

I have a Sharepoint 2013 application and need to prevent users from login from differents places at same time.
Searching for a good solution I found out that giving a new session token each time the user login is a good solution. Also I will have to expires all older tokens from that user.
How can I do this using c#.Net ?
Try to do it like in normal ASP.NET app, the only difference would be to use session start from HttpModule, not from global.asax.
In your case do following:
Create custom IHttpModule
public class CustomSessionHttpModule : IHttpModule
{
public void Init(HttpApplication context)
{
IHttpModule httpModule = context.Modules.Get("Session");
var sessionModule = httpModule as SessionStateModule;
if (sessionModule != null)
{
sessionModule.Start += OnSessionStart;
}
}
public void Dispose()
{
}
private void OnSessionStart(object sender, EventArgs e)
{
//Check if user has active session( in your storage) if yes then invalidate it or block new request
}
}
Add module to the web.config in the farm with SPWebConfigModification to
configuration/system.webServer/modules view feature (as a PoC do it manually)
Ensure that session is enabled in the web.config (as far I remember this is required)
I've tried a lot of solutions to this. I've tried to deal with Sessions, Cookies without success.
My solution was to use some way of verification like IP for instance.
After check the IP (if the user IP changed) the program redirects the user to the following page:
[sharepointsite]/_layouts/closeconnection.aspx?loginasanotheruser=true
This page is from olders versions of Sharepoint and is hidden in the 2013 version.
It forces the user to log in again.
Here is some code example:
if(currentIP != lastIP)
{
updateIP(currentIP);
Response.Write("<sharepointsite>/_layouts/closeconnection.aspx?loginasanotheruser=true");
}
I know this is not the best solution, but was the only one that worked for me.

On handling any event related to login / logout activity

Currently, my application can track whenever user has logged in or out. The logging out activity is saved to database because User simply click a button. However, I would be interested in how to implement something similar but when user's ticket is no longer valid, either because it's been removed or expired.
I would like to handle any event (if possible) that is fired whenever user is no longer valid.
Is it possible to implement anything like this?
Cheers!
EDIT
I found two solutions. One uses BaseController--the code below from Handle asp.net MVC session expiration. This is simply what the author claims the easiest and working solution. First, is it a good idea to inherit all conrollers from a custom controllers? Second, I have already customized my own [Authorize] attribute. I don't know how all this together is going to work, and three, can i still have access to the cookie of a logged user where I hold UserID and some other data such as UserRole? And also it is not triggered automatically but whenver user trying to access and cookie is expired. It could be 6 hours later.
public class BaseController : Controller
{
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
// Check if session exists
if (filterContext.HttpContext.Session != null)
{
//if new session
if (filterContext.HttpContext.Session.IsNewSession)
{
string cookie = filterContext.HttpContext.Request.Headers["Cookie"];
//if cookie exists and session id index is greater than zero
if ((cookie != null) && (cookie.IndexOf("ASP.NET_SessionId") >= 0))
{
//redirect to desired session expiration action and controller
filterContext.Result = RedirectToAction("SessionExpired", "Home");
return;
}
}
}
//otherwise continue with action
base.OnActionExecuting(filterContext);
}
}
The second solution I found is based on Global.aspx's function
void Session_End(object sender, EventArgs e)
{
Response.Redirect("~/Account/Timeout");
}
but I read it only works when I use InProc. I don't know what that is, though.
If you go with the filter [Authorize] you can add it as a global filter.
filters.Add(new AuthorizeAttribute());
Which is what your controller does too.
To answer your questions:
Yes, if you are building a framework that impacts all controllers. There is no problem with inheriting from a base controller.
Looks like the global filter is right up your alley.
You can't access the cookie once the session expires since there is no client!
When a session expires, the screen gets frozen to information they've already been authorized for until any subsequent request is made.
Hope that helps.

When is Session State ready for use in asp.net

I have an asp.net site, where in i am checking session variable in Global.asax in Application_BeginRequest but it always says object reference not set to an instance of an object, i don't understand this, because i am checking the condition for null before using the value, but still it throws the above error, however when i check it inside the default.aspx Page_Load event it works properly without any issues.
Can anyone tell whats the problem behind this, am i not supposed to use the session variable inside Application_BeginRequest
If yes, then how i will be checking the session value, what i want to achieve is, if user is logged in (If Session["Login"] is not empty means user logged in) and has the rights to access the page, then allow him/her else throw him to the homepage.
Here is what i am doing.
Below function checks for if user is logged in:
public static String LoggedInUser
{
get
{
if (HttpContext.Current.Session["Login"] == null)
return String.Empty;
else
return HttpContext.Current.Session["Login"].ToString();
}
set
{
HttpContext.Current.Session["Login"] = value;
}
}
Below function checks if user has right to access the page:
public static bool IsPageAllowed(String Pagename)
{
bool _isPageAllowed = false;
XmlDocument doc = new XmlDocument();
doc.Load(HttpContext.Current.Server.MapPath("Pagenames.xml"));
if (LoggedInUser != String.Empty)
{
XmlNodeList list = doc.DocumentElement.SelectNodes("/AllPages/Pages[#user='" + GetLoggedInUserRole(Globals.LoggedInUser).ToLower() + "']/Page[contains(text(), '" + Pagename + "')]");
if (list.Count > 0)
_isPageAllowed = true;
}
return _isPageAllowed;
}
And below function is used on Application_BeginRequest to redirect user based on their rights:
if (!Globals.IsPageAllowed(rawUrl.Substring(1, rawUrl.Length - 1)))
{
Response.Redirect("default.aspx");
}
Session state is available during and after appropriately named HttpApplication.PostAcquireRequestState.
Occurs when the request state (for example, session state) that is associated with the current request has been obtained.
Full sequence of events with descriptions is available somewhere on [MSDN](http://msdn.microsoft.com/en-us/library/bb470252.aspx and other sites like ASP.NET Application Life Cycle
Shortened list of events below (many events are omitted, see MSDN link for details):
BeginRequest
AuthenticateRequest
AcquireRequestState
PostAcquireRequestState
ProcessRequest method (or the asynchronous version IHttpAsyncHandler.BeginProcessRequest) of the appropriate IHttpHandler class for the request. For example, if the request is for a page, the current page instance handles the request.
Note that session state is not available till at least AcquireRequestState (where it may be available if SessionStateModule managed to receive that event before your code). There is no way Session will be available during BeginRequest.
Note that there are explicit authentication events that should be used if you need authentication/authorization (also it is not usable for your case as you keep auth information in session state).

ASP.NET User Authentication across multiple projects

I am building an ASP.NET UI on an existing system, which consists of separate SQL server databases for each project. An "enterprise" database lists all current projects which allows anonymous users to select the project to work in. The project name is stored in a session variable. When log in is required the username/password/roles etc are obtained from the database indicated by the project name. I have implemented my own basic membership and role providers to do this, with changes in web.config to specify the roles required for specific pages. (I do not use the standard ASP.NET Configuration tool to manage users, I have existing apps that work with my user tables).
This all seemed to work initially but I discovered that the session variables are not yet loaded at the time when the authorization system checks the roles the current user belongs to in order to determine if the page is accessible. So if we have a < allow roles="xxx" > in web.config then the authorization system fires before session data is loaded and thus before I know which project database should be used.
[Specifically: HttpContext.Current.Session is null when the call to RoleProvider.GetRolesForUser is made]
Anybody who has tackled this problem should know exactly what I'm talking about. My questions therefore are:
A) What is the "Best Practise" solution to this scenario?
B) Could I be storing the project name somewhere else (not in session variable) that is available during the authorization phase?
[Update: Yes - we can use cookies, assuming we do not require cookieless operation]
C) Is there a way to manually get the session variable at this earlier time?
I tried an option to cache roles in cookies, but after a few minutes of testing with that option on I found GetRolesForUsers was still being called.
Thanks
Update:
Here is another description of the root problem which suggests "The application could cache this information in the Cache or Application objects.":
http://connect.microsoft.com/VisualStudio/feedback/details/104452/session-is-null-in-call-to-getrolesforuser
Update:
This looks like the same problem found here:
Extending the RoleProvider GetRolesForUser()
Update:
There was a suggestion about using UserData in FormsAuthenticationTicket, but I require this data even when not logged on.
UPDATE: I solve this these days in a much simpler way by using a wildcard SSL certificate that allows me to configure subdomains for each project, thus the project selection is specified directly in the URL (and each project gets its own subdomain). I still use a cookie hack purely for testing purposes when running on localhost where we have no subdomains.
Original solution:
I have not found any "best practise" write up on this scenario, but here is what I have settled on:
1) In order to support anonymous users switching between projects (i.e. SQL databases) I simply use a session variable to track the project selection. I have a global property that uses this project selection to serve the corresponding SQL connection string as and when it is required.
2) In order to support the call to GetRolesForUser() on pages that have role restrictions applied to them we cannot use the session variable, because as stated the session variable has not been initialized yet when GetRolesForUser() is actually called (and I have found no way to force it into being at this early point in the request cycle).
3) The only option is to use a cookie, or use the Forms Authentication ticket's UserData field. I trawled through many theories about using session/cookie/IDs linked to an object stored in the application cache (which is available when the session is not) but ultimately the correct choice is to place this data in the authentication ticket.
4) If a user is logged on to a project it is via a ProjectName/UserName pair, hence anywhere we are tracking the user's authentication we require both these data. In trivial testing we can get away with the username in the ticket and the projectname in a separate cookie, however it is possible for these to get out of synch. For example if we use a session cookie for the projectname and tick "remember me" when we logon (creating a permanent cookie for the authentication ticket) then we can end up with a username but no projectname when the session cookie expires (browser is closed). Hence I manually add the project name to the UserData field of the authentication ticket.
5) I have not figured out how to manipulate the UserData field without explicitly setting a cookie, which means that my solution cannot work in "cookieless" session mode.
The final code turned out to be relatively simple.
I override the Authenticate event of the LoginView in the login page:
//
// Add project name as UserData to the authentication ticket.
// This is especially important regarding the "Remembe Me" cookie - when the authentication
// is remembered we need to know the project and user name, otherwise we end up trying to
// use the default project instead of the one the user actually logged on to.
//
// http://msdn.microsoft.com/en-us/library/kybcs83h.aspx
// http://msdn.microsoft.com/en-us/library/system.web.ui.webcontrols.login.remembermeset(v=vs.100).aspx
// http://www.hanselman.com/blog/AccessingTheASPNETFormsAuthenticationTimeoutValue.aspx
// http://www.csharpaspnetarticles.com/2009/02/formsauthentication-ticket-roles-aspnet.html
// http://www.hanselman.com/blog/HowToGetCookielessFormsAuthenticationToWorkWithSelfissuedFormsAuthenticationTicketsAndCustomUserData.aspx
// http://stackoverflow.com/questions/262636/cant-set-formsauthenicationticket-userdata-in-cookieless-mode
//
protected void LoginUser_Authenticate(object sender, AuthenticateEventArgs e)
{
string userName = LoginUser.UserName;
string password = LoginUser.Password;
bool rememberMe = LoginUser.RememberMeSet;
if ( [ValidateUser(userName, password)] )
{
// Create the Forms Authentication Ticket
FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(
1,
userName,
DateTime.Now,
DateTime.Now.AddMinutes(FormsAuthentication.Timeout.TotalMinutes),
rememberMe,
[ ProjectName ],
FormsAuthentication.FormsCookiePath);
// Create the encrypted cookie
HttpCookie cookie = new HttpCookie(FormsAuthentication.FormsCookieName, FormsAuthentication.Encrypt(ticket));
if (rememberMe)
cookie.Expires = DateTime.Now.AddMinutes(FormsAuthentication.Timeout.TotalMinutes);
// Add the cookie to user browser
Response.Cookies.Set(cookie);
// Redirect back to original URL
// Note: the parameters to GetRedirectUrl are ignored/irrelevant
Response.Redirect(FormsAuthentication.GetRedirectUrl(userName, rememberMe));
}
}
I have this global method to return the project name:
/// <summary>
/// SQL Server database name of the currently selected project.
/// This name is merged into the connection string in EventConnectionString.
/// </summary>
public static string ProjectName
{
get
{
String _ProjectName = null;
// See if we have it already
if (HttpContext.Current.Items["ProjectName"] != null)
{
_ProjectName = (String)HttpContext.Current.Items["ProjectName"];
}
// Only have to do this once in each request
if (String.IsNullOrEmpty(_ProjectName))
{
// Do we have it in the authentication ticket?
if (HttpContext.Current.User != null)
{
if (HttpContext.Current.User.Identity.IsAuthenticated)
{
if (HttpContext.Current.User.Identity is FormsIdentity)
{
FormsIdentity identity = (FormsIdentity)HttpContext.Current.User.Identity;
FormsAuthenticationTicket ticket = identity.Ticket;
_ProjectName = ticket.UserData;
}
}
}
// Do we have it in the session (user not logged in yet)
if (String.IsNullOrEmpty(_ProjectName))
{
if (HttpContext.Current.Session != null)
{
_ProjectName = (string)HttpContext.Current.Session["ProjectName"];
}
}
// Default to the test project
if (String.IsNullOrEmpty(_ProjectName))
{
_ProjectName = "Test_Project";
}
// Place it in current items so we do not have to figure it out again
HttpContext.Current.Items["ProjectName"] = _ProjectName;
}
return _ProjectName;
}
set
{
HttpContext.Current.Items["ProjectName"] = value;
if (HttpContext.Current.Session != null)
{
HttpContext.Current.Session["ProjectName"] = value;
}
}
}
Can't you postback the project selection to some page, add that selection to the session, then redirect to appropriate protected page, where auth will kick in and force login?
ASP.NET session doesn't get created in the form of a cookie until you place at least one item in it.

Categories

Resources