I have built a shopping cart that uses Session State to keep the shopping cart data while the user is browsing the store.
I have an issue where if I leave the browser window open for a long time on step1 of the shopping cart, then press "go to step 2", my actions throw an error because the step2 action assumes the session hasn't expired and the ShopCart object is in the correct state.
I would like this scenario to be nicer for my users, but I think i need to somehow detect if the session has expired so that on next request I can throw them to Step1.
I found the following code that claims to to solve the problem, but it doesn't work for me.
The IsNewSession condition is true but the condition
if ((null != sessionCookie) && (sessionCookie.IndexOf("ASP.NET_SessionId") >= 0)) {
// handle expired session
}
always returns false and it never handles the invalid session. I'm confused.
Is this possible in ASP.NET (and MVC)?
Way 1
Put this code in the Init / Load event of Page 2...
if (Context.Session != null)
{
if (Context.Session.IsNewSession)
{
string sCookieHeader = Request.Headers["Cookie"];
if ((null != sCookieHeader) && (sCookieHeader.IndexOf("ASP.NET_SessionId") >= 0))
{
if (Request.IsAuthenticated)
{
FormsAuthentication.SignOut();
}
Response.Redirect("Error Page");
}
}
}
Way 2
Alternative you can check whether the Session object exists before proceeding to work with it in Page 2, like this:
if (Session["Key"] != null)
{
Object O1 = (Object) Session["Key"];
}
else
{
Response.Redirect("ErrorPage.aspx");
}
The King 's answer does not work for me. I have added FormsAuthentication.SignOut() in OnActionExcuting(). The Response.Redirect will not work!
if (Request.IsAuthenticated)
{
FormsAuthentication.SignOut();
}
This is my complete method
public class SessionExpireFilterAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
HttpContext ctx = HttpContext.Current;
// check if session is supported
if (ctx.Session != null)
{
// check if a new session id was generated
if (ctx.Session.IsNewSession)
{
// If it says it is a new session, but an existing cookie exists, then it must
// have timed out
string sessionCookie = ctx.Request.Headers["Cookie"];
if ((null != sessionCookie) && (sessionCookie.IndexOf("ASP.NET_SessionId") >= 0))
{
string redirectOnSuccess = filterContext.HttpContext.Request.Url.PathAndQuery;
string redirectUrl = string.Format("?ReturnUrl={0}", redirectOnSuccess);
string loginUrl = FormsAuthentication.LoginUrl + redirectUrl;
if (ctx.Request.IsAuthenticated)
{
FormsAuthentication.SignOut();
}
RedirectResult rr = new RedirectResult(loginUrl);
filterContext.Result = rr;
//ctx.Response.Redirect("~/Home/Logon");
}
}
}
base.OnActionExecuting(filterContext);
}
}
You need to create the Session_OnEnd method In Global.asax.cs file in your project.
this is my code and I am able to Detecting Session expiry on ASP.NET MVC
protected void Session_OnEnd(object sender, EventArgs e)
{
int userid = 0;
userid = Convert.ToInt32(Session["UserID"]);
if (userid != 0)
{
var userActivity = DependencyResolver.Current.GetService<IUserRepo>();
var responce = userActivity.LogOutUsers(userid);
if (responce == true)
{
Session.Clear();
Session.Abandon();
}
}
}
more
Related
I tried to prevent user for changing the "ASP.NET_SessionId"
I tried this code:
Response.Cookies["ASP.NET_SessionId"].Value = GenerateHashKey();
But my session (Session["userId"]) was removed when I tried to set the cookie
Here is some code that I tried without success:
protected void Application_BeginRequest(object sender, EventArgs e)
{
//Check If it is a new session or not , if not then do the further checks
if (Request.Cookies["ASP.NET_SessionId"] != null && Request.Cookies["ASP.NET_SessionId"].Value != null)
{
string newSessionID = Request.Cookies["ASP.NET_SessionID"].Value;
//Check the valid length of your Generated Session ID
if (newSessionID.Length <= 24)
{
//Log the attack details here
Response.StatusCode = 401;
}
//Genrate Hash key for this User,Browser and machine and match with the Entered NewSessionID
if (GenerateHashKey() != newSessionID.Substring(24))
{
//Log the attack details here
Response.StatusCode = 401;
//throw new HttpException("401");
}
//Use the default one so application will work as usual//ASP.NET_SessionId
Request.Cookies["ASP.NET_SessionId"].Value = Request.Cookies["ASP.NET_SessionId"].Value.Substring(0, 24);
}
}
private string GenerateHashKey()
{
StringBuilder myStr = new StringBuilder();
myStr.Append(Request.Browser.Browser);
myStr.Append(Request.Browser.Platform);
myStr.Append(Request.Browser.MajorVersion);
myStr.Append(Request.Browser.MinorVersion);
SHA1 sha = new SHA1CryptoServiceProvider();
byte[] hashdata = sha.ComputeHash(Encoding.UTF8.GetBytes(myStr.ToString()));
return Convert.ToBase64String(hashdata);
}
protected void Application_EndRequest(object sender, EventArgs e)
{
//Pass the custom Session ID to the browser.
if (Response.Cookies["ASP.NET_SessionId"] != null)
{
Response.Cookies["ASP.NET_SessionId"].Value = Request.Cookies["ASP.NET_SessionId"].Value + GenerateHashKey();
}
}
How to prevent the user for set session
Looks like you are trying to secure your session value from tampering? If you set the value yourself, you override the session identifier and destroy the purpose of the asp session cookie.
ASP.Net_SessionId is a cookie which is used to identify the users session on the server. The session being an area on the server which can be used to store data in between http requests.
If you are trying to solve the session fixation problem, which is the only vulnerability of asp session cookie, then you need to introduce a new cookie like this: https://medium.com/#grep_security/session-fixation-broken-authentication-and-session-management-c37ce0111bf5
I have a web system developed with ASP.NET MVC 4.
We have a user management that allows users to edit/delete other users.
On delete function, currently i'm doing only a delete on database.
So here is my login controller/method:
[HttpPost]
public ActionResult Login(LoginViewModel loginViewModel)
{
if (_loginService == null)
_loginService = new LoginService();
var result = _loginService.Login(loginViewModel.User, loginViewModel.Password);
if (!result.Error)
{
var userData = JsonConvert.SerializeObject(result.User);
FormsAuthentication.SetAuthCookie(result.User.Id, false);
var ticket = new FormsAuthenticationTicket(1, result.Id, DateTime.Now, DateTime.Now.AddMinutes(9999), true, userData, FormsAuthentication.FormsCookiePath);
var encryptedCookie = FormsAuthentication.Encrypt(ticket);
var cookie = new HttpCookie(FormsAuthentication.FormsCookieName, encryptedCookie) { Expires = DateTime.Now.AddHours(14) };
Response.Cookies.Add(cookie);
}
return new JsonResult
{
Data = result
};
}
And I treat that return on client side with some javascript. This is working fine by now.
For every Controller that user must be authenticated, I have [Authorize] attribute.
Lets say that I just logged in with user ABC. As long as ABC cookie is alive he can navigate fine.. the problem is when some user (lets say ZXC) deletes user ABC, he will still navigate fine until the cookie expires.
Is there a way to drop ABC session on IIS in the moment ZXC deletes him from database?
I don't know.. force a cookie expire. I just don't wanna implement a consult for each action done in navigation to check if the user is still "alive" in database.
Any ideas, suggestions?
Firstly, no. There is no way to access cookies in another session as they only exist for the lifetime of the request/response. However, you could store a static List of all current authenticated users and invalidate them that way.
This is a bit problematic because in the case that the App Pool recycles - all users will be 'logged out'. If this is not an issue for you (i.e. the app pool recycles at 2am and it is for a business system that does not operate at 2 am) then you can try this...
Code provided is untested
source: https://msdn.microsoft.com/en-us/library/system.web.security.formsauthenticationmodule.authenticate
EDIT:
I was not removing the cookie from the request and expiring it in the response.
In the Global.asax
private static List<string> _authenticatedUsers = new List<string>();
public static AuthenticateUser (MyApplicationUser user)
{
if(!_authenticatedUsers.ContainsKey(user.Username))
{
_authenticatedUsers.Add(user.Username);
}
}
public static DeauthenticateUser (MyApplicationUser user)
{
if(_authenticatedUsers.ContainsKey(user.Username))
{
_authenticatedUsers.Remove(user.Username);
}
}
public void FormsAuthentication_OnAuthenticate(object sender, FormsAuthenticationEventArgs args)
{
if (FormsAuthentication.CookiesSupported)
{
if (Request.Cookies[FormsAuthentication.FormsCookieName] != null)
{
try
{
FormsAuthenticationTicket ticket = FormsAuthentication.Decrypt(
Request.Cookies[FormsAuthentication.FormsCookieName].Value);
MyApplicationUser user = JsonConvert.DeserializeObject(ticket.UserData);
if(user == null || !_authenticatedUsers.Any(u => u == user.Username))
{
// this invalidates the user
args.User = null;
Request.Cookies.Remove(FormsAuthentication.FormsCookieName);
HttpCookie myCookie = new HttpCookie(FormsAuthentication.FormsCookieName);
DateTime now = DateTime.Now;
myCookie.Value = "a";
myCookie.Expires = now.AddHours(-1);
Response.Cookies.Add(myCookie);
Response.Redirect(FormsAuthentication.LoginUrl);
Resonpse.End();
}
}
catch (Exception e)
{
// Decrypt method failed.
// this invalidates the user
args.User = null;
Request.Cookies.Remove(FormsAuthentication.FormsCookieName);
HttpCookie myCookie = new HttpCookie(FormsAuthentication.FormsCookieName);
DateTime now = DateTime.Now;
myCookie.Value = "a";
myCookie.Expires = now.AddHours(-1);
Response.Cookies.Add(myCookie);
Response.Redirect(FormsAuthentication.LoginUrl);
Resonpse.End();
}
}
}
else
{
throw new HttpException("Cookieless Forms Authentication is not " +
"supported for this application.");
}
}
In your login action
public ActionResult Login(LoginViewModel loginViewModel)
{
...
if (!result.Error)
{
...
MvcApplication.AuthenticateUser(result.User);
...
}
...
}
In your logout action
public ActionResult Logout(...)
{
...
MvcApplication.DeauthenticateUser(user);
...
}
In your delete method
...
MvcApplication.DeauthenticateUser(user);
...
When I attempt to access a cookie, I get the following exception:
Object reference not set to an instance of an object.
This is the line in question:
if (Request.Cookies["selectBoxValue"].Value != null)
Controller
[Authorize]
public ActionResult Index()
{
if (Request.Cookies["selectBoxValue"].Value != null)
{
HttpCookie groupId = new HttpCookie("selectBoxValue");
groupId = Request.Cookies["selectBoxValue"];
// Collect all comments connected to current group
int t = Convert.ToInt32(groupId.Value);
pv.ListofComments = db.Comments.Where(dp => dp.GroupID == t).ToList();
// Show only groups connected to current user
var CurrentUser = User.Identity.GetUserId();
var groupUser = db.GroupUsers.Where(u => u.ApplicationUserId == CurrentUser).Select(gr => gr.GroupId).ToList();
pv.GroupList = db.Groups.Where(g => groupUser.Contains(g.Id));
return View(pv);
}
Your error is because something in that chain is not there:
if (Request.Cookies["selectBoxValue"].Value != null)
Things to check:
var myCookie = Request.Cookies["selectBoxValue"];
myCookie!= null;
myCookie.Length > 0;
Most likely you don't have a cookie coming in on the Request that's called selectBoxValue. Since selectBoxValue (from its name) sounds like it's something on your form itself, I'm curious as to why you'd be checking a cookie for it? If it's a persisted value from a previous page (not the one that sent the Request to the server), then call it something more intuitive than selectBoxValue.
To learn more about how to write a cookie and to read a cookie; see this Stack Overflow answer.
If you expect the user to have a cookie called selectBoxValue and they don't, then you have a specific issue: wherever you're setting that cookie, it's not getting sent to the user in the Response.
If you are OK with that (that is, some codepaths expect that cookie and others do not) then you can set the object you're playing with to a sane value if that cookie doesn't exist:
int defaultGroupId = 1;
var obj = Request.Cookies["selectBoxValue"] ?? defaultGroupId;
Your code also has some issues:
if (Request.Cookies["selectBoxValue"].Value != null)
{
HttpCookie groupId = new HttpCookie("selectBoxValue"); //why create one?
groupId = Request.Cookies["selectBoxValue"];
// Collect all comments connected to current group
int t = Convert.ToInt32(groupId.Value);
}
Why are you creating a cookie only not to use it?
A Cookie is only meant to be created if you're going to send it somewhere. So here you should write something like:
int defaultGroupId = 1;
int groupId = defaultGroupId;
if (Request.Cookies["selectBoxValue"] != null) {
var parsed = Int.TryParse(Request.Cookies["selectBoxValue"].Value, out groupId);
if (!parsed) {
// the incoming cookie value wasn't an integer or couldn't be parsed to integer. In this case do you want to set it to an appropriate value (say whatever comes out of your query?
}
//At this point you either have a valid `groupId` or you have your `defaultGroupId` so you can use `groupId` normally.
}
You are checking if the value of your cookie is null, which it may or may not be. If the cookie hasn't been set yet, then the cookie itself is null, which means you get an exception if you tryto access the value of the cookie.
Here's a code snippet of what you need to check.
//retrieve the cookie
var cookieSelectBoxValue = Request.Cookies["selectBoxValue"];
//Check if the cookie itself is null
if(cookieSelectBoxValue != null)
{
//Retrieve the value of the cookie
var cookieValue = cookieSelectBoxValue.Value;
//Check if the value of the cookie is null
if(cookieValue!= null)
{
//The rest of your logic goes here
}
}
To set the cookie, you can use the Response.SetCookie() method in your Controller Action. Here's a quick example:
public ActionResult Index()
{
if (Request.Cookies["selectBoxValue"] != null)
{
var cookie = new HttpCookie("selectBoxValue")
{
Expires = DateTime.Now.AddYears(1),
Value = "CookieValueGoesHere"
};
Response.Cookies.Add(cookie);
}
return View("Index");
}
My application has a Session Fixation Vulnerability. To fix that vulnerability, I am changing the session id after login.
I am having below web pages in different folders Master and Transaction.
~/Master/Login.aspx : After the credentials validated, I am setting
Response.Cookies["ASPFIXATION"].Value ="xyz";
Session["ASPFIXATION"] = "xyz"
and redirect to ~/Master/Home.aspx
~/Master/Home.aspx : In this page, I am checking the session value with the cookie value on page load event.
string cookie_value = string.Empty;
string session_value = string.Empty;
if (Request.Cookies["ASPFIXATION"] != null)
cookie_value = Request.Cookies["ASPFIXATION"].Value;
if (HttpContext.Current.Session["ASPFIXATION"] != null)
session_value = HttpContext.Current.Session["ASPFIXATION"].ToString();
if (cookie_value != g_SessionFix)
{
if (Request.Cookies["ASP.NET_SessionId"] != null)
{
Response.Cookies["ASP.NET_SessionId"].Value = null;
Response.Cookies["ASP.NET_SessionId"].Expires = DateTime.Now.AddDays(-1);
}
if (Request.Cookies["ASPFIXATION"] != null)
{
Response.Cookies["ASPFIXATION"].Value = null;
Response.Cookies["ASPFIXATION"].Expires = DateTime.Now.AddDays(-1);
}
Response.Redirect("~/Master/Login.aspx", false);
}
Here the Session has the value and it is validated.
~/Transaction/Report.aspx : If i redirect to this page and have to check the same session and cookie value logic in this page load. But here the value of HttpContext.Current.Session["ASPFIXATION"] is null and value of HttpContext.Current.Session.IsNewSession also true
Try to reassign the value after reading the values, sometimes it clears the values after reading from the session:
if (Request.Cookies["ASPFIXATION"] != null)
{
cookie_value = Request.Cookies["ASPFIXATION"].Value;
Request.Cookies["ASPFIXATION"].Value = cookie_value;
}
if (HttpContext.Current.Session["ASPFIXATION"] != null)
{
session_value = HttpContext.Current.Session["ASPFIXATION"].ToString();
HttpContext.Current.Session["ASPFIXATION"] = session_value;
}
If it still doesn't work for you then you have one other option to do it,
if your session data is not confidential then try to pass it in query string and on page redirection retrieve this value.
Response.Redirect("~/Master/Login.aspx?data=" + session_value +");
I have a MVC project that I'm using Forms Authentication, and I had to implement Roles for certain pages, so I got this in global.asax:
protected void Application_AuthenticateRequest(Object sender, EventArgs e)
{
HttpCookie authCookie = Context.Request.Cookies[FormsAuthentication.FormsCookieName];
if (authCookie == null || authCookie.Value == "")
{
return;
}
FormsAuthenticationTicket authTicket;
try
{
authTicket = FormsAuthentication.Decrypt(authCookie.Value);
}
catch
{
return;
}
string[] roles = authTicket.UserData.Split(';');
if (Context.User != null)
{
Context.User = new GenericPrincipal(Context.User.Identity, roles);
}
}
And I save the user roles when I log in my model:
//After checking login/password
HttpCookie cookie = HttpContext.Current.Request.Cookies.Get(FormsAuthentication.FormsCookieName);
if (cookie == null)
{
cookie = new HttpCookie(FormsAuthentication.FormsCookieName);
HttpContext.Current.Response.Cookies.Add(cookie);
}
string userRoles = null;
if (users[i].PerfilID == UserRanks.Admin)
{
userRoles = "Admin;Users";
}
else
{
userRoles = "Users";
}
FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(0,
users[i].Name,
DateTime.Now,
DateTime.Now.AddDays(1),
false,
userRoles,
FormsAuthentication.FormsCookiePath
);
cookie.Value = FormsAuthentication.Encrypt(ticket);
HttpContext.Current.Response.Cookies.Set(cookie);
HttpContext.Current.Session["UserID"] = users[i].UserID;
HttpContext.Current.Session["LastLogin"] = users[i].LastLogin;
HttpContext.Current.User = new GenericPrincipal(HttpContext.Current.User.Identity, userRoles.Split(';'));
To retrieve the values of the session variables, I have a static property:
public static int UserID
{
get
{
object sessionData = HttpContext.Current.Session["UserID"];
if (sessionData == null)
{
//I had something here...
return 0;
}
return Convert.ToInt32(sessionData);
}
private set { }
}
Before I implemented roles authorization, I used to save the UserID in the cookie userData, and if the UserID in the session, when requested, was null, I'd retrieve it from the cookie instead (and set it to the session again).
Now the userData is being used to the roles management and I'm having issues with the session dropping faster than the cookie expiration, and I have users logged in which I can't retrieve their UserIDs and thus fails for all operations.
So I have to put a checker in each controller function such as:
if (MyUser.Session.UserID == 0)
{
//redirect to Login
}
...which defeats the whole purpose of using [Authorize] I guess.
Is there a better way to handle login expiration and session variables like this?
I thought about using JSON or some other format and save a bunch of userData in the cookie, but I'd like something simpler, if possible.
Also, I'm doing this in the login:
HttpContext.Current.User = new GenericPrincipal(HttpContext.Current.User.Identity, userRoles.Split(';'));
Which seems to be about the same the AuthenticateRequest does (I got that part from elsewhere, seemed to be the recommended way to handle member roles), but it doesn't work like it should. The user always gets redirected out on the [Authorize(Roles="Admin")] or even the [Authorize] only functions if I leave only that (and remove the global.asax part). Why?