Hope someone can help me with this cause this problem has been bugging me for some time now.
I'm building a webshop and i'm integrating with a payment system hosted by a third party.
I have people review their order and when they click to proceed to entering in their credit card information i send them off through a secure connection to the payment system hosted on a different server, providing them with an "OK"-URL for sending them back to my site so i can close the order and do some other stuff, after a successful payment.
The problem is in Firefox when the remote server redirects back to my server the session id is lost and the forms authentication cookie is a completely different one, resulting in the user being logged out. This is not the case in IE, Safari and Chrome.
Take from Fiddler2 before redirecting to remote host
Request sent 342 bytes of Cookie data:
ASP.NET_SessionId=uusldflsxdecn0cfkoaw11tx;
.ASPXAUTH=90109DFD47272DDB02905800582D[....]1878E319DED0DD63BBCA07C1114CD02EA32E7FE3C28BB3ECA07D3EF2131E7425AB6AF4D48777FBE9F965675CA9D0A8CF66AF4433F2CE045ADB419317F9C6F04D32669EF5EB87135B9949EC4462F2826BB6B
After remote host redirects back to my server
Request sent 298 bytes of Cookie data:
.ASPXAUTH=A73EE1BE818E1FE934F0AD8D48078E26646318DA24886CAE9[...]23AD63D679D0D838E746DE6357DBE0D3EA32A634873A62F399E1033138648314E83F1282A1949D64F4FC833948F1D3ABA5D997D8CABE3C6CA04256404A25FFC8C3D427A7640B488B01532314EE68
This seems really strange to me, and i haven't been able to find a way around this. My process for doing things works perfectly in IE, Safari and Chrome..
Is this a problem on the remote server? Is there something i'm doing wrong?
Is there a way of reconstructing the session somehow?
From web.config:
<authentication mode="Forms">
<forms loginUrl="~/Account/Login" timeout="1440"/>
</authentication>
<sessionState mode="InProc" customProvider="DefaultSessionProvider">
<providers>
<add name="DefaultSessionProvider" type="System.Web.Providers.DefaultSessionStateProvider, ..." connectionStringName="DefaultConnection"/>
</providers>
</sessionState>
I still don't know why the above problem occurs, but after sleeping on it I figured out a pretty basic solution. I simply save the cookie values in my database before redirecting people to the remote host, and upon return i check the cookie values in the request and if there's a discrepancy i restore them with the ones from the database.
If the session key is lost upon return though, i have to make one redirect with the restored session cookies before accessing anything put in the session, for which i just use a RedirectToAction call, to another action in the same controller.
private void SaveCookie(int orderId)
{
var sessionCookie = Request.Cookies.Get("ASP.NET_SessionId");
var authCookie = Request.Cookies.Get(".ASPXAUTH");
var ci = new CookieInformation
{
OrderId = orderId,
SessionCookie = sessionCookie.Value,
AuthCookie = authCookie.Value
};
Database.SetCookieInformation(ci);
Logger.Info("Saved sessionId: " + sessionCookie.Value + " auth: " + authCookie.Value, this.ToString());
}
And after returning:
private void RestoreCookies(int orderId)
{
var reqSessionCookie = Request.Cookies.Get("ASP.NET_SessionId");
var reqAuthCookie = Request.Cookies.Get(".ASPXAUTH");
var savedCookie = Database.GetCookieInformation(orderId);
if (reqSessionCookie.Value != savedCookie.SessionCookie || reqAuthCookie.Value != savedCookie.AuthCookie)
{
Response.Cookies.Clear();
Response.Cookies.Set(new HttpCookie("ASP.NET_SessionId", savedCookie.SessionCookie));
Response.Cookies.Set(new HttpCookie(".ASPXAUTH", savedCookie.AuthCookie));
}
}
Related
I have an API action that returns a HttpResponseMessage.
API address is like: http://localhost/login?authcode=xxx
The API action does some login authentication and redirects the user to either register or the welcome page. Code goes like:
var response = new HttpResponseMessage();
var cookie = new CookieHeaderValue("token", "ThisIsTheTokenNeeded");
response.Headers.AddCookies(new CookieHeaderValue[] { cookie });
response.StatusCode = HttpStatusCode.Found;
response.Headers.Location = new Uri("http://localhost/welcome.html");
return response;
In welcome.html, I use "document.write(document.cookie)" and cannot see the cookie named "token". Some how it got lost. Could anyone tell me how to get this done or this architecture is not correct after all?
I found the answer. The scope is not set. In my original code the following line is missing.
cookie.Path = "/";
Because redirecting to another page, even if under the same domain, the cookie is not valid across different pages. If path is not set, then the cookie is only valid with the original request targeting http://localhost/login?authcode=xxx
Today I learnt that I need to carefully examine the domain and the path attribute of the cookie before claiming that somebody ate it.
My cookies have added Path, but the problem still not resolve.
After a long time, I finally resolve this problem by remove session state config in web.config:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<system.web>
<!--<sessionState cookieless="false" timeout="30" mode="StateServer" stateConnectionString="tcpip=localhost:42424" />-->
</system.web>
</configuration>
I can add set-cookie header after comments <sessionState>.
I hope this helps you, thanks.
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?
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;
}
}
I've seen multiple articles like this one that explain how to detect that a user's session has timed out. And for clarity's sake, these articles are referring to the timeout value defined by this web.config line:
<sessionState mode="InProc" cookieless="UseDeviceProfile" timeout="120" />
Not to get into that method too much, but this involves checking that Session.IsNewSession is true and that a session cookie already exists. But I haven't seen any articles on how to detect authentication timeout -- the one defined by this web.config line:
<authentication mode="Forms">
<forms loginUrl="~/Home/Customer" timeout="60" name=".ASPXAUTH" requireSSL="false" slidingExpiration="true" defaultUrl="~/Home/Index" cookieless="UseDeviceProfile" enableCrossAppRedirects="false"/>
</authentication>
Multiple articles online, including this SO post, have said that your Session timeout value should generally be double your Authentication timeout value. So right now, as above, my Session is 120 and my Authentication is 60. This means that I'll never get in a situation where the Session has timed out, but the user is still Authenticated; if the user ever times out, it will be due to Authentication, not Session.
So, like everyone else, I'm interested in how to report to the user that their session has timed out (but really it'll be due to the Authentication timeout). Does anyone know of a way to accomplish this, or any resources online that can point me to a solution?
This is probably not the optimum approach, but here's something I thought of.
On login, record a timestamp in the session marking when the user logged in. On each subsequent request (maybe in the global.asax BeginRequest?), compare this timestamp to the current time, and match this up with the authentication timeout (Scott Hanselman explains how to read it here).
That's my "off top of my head" thought anyhow...
I would leverage the http pipeline early in the request and send an apporpriate response.
My source for answering such questions is:
Professional ASP.NET 2.0 Security, Membership, and Role Management
An HTTP module might do the trick:
Web.config:
<configuration>
<system.web>
<httpModules>
<add name="MyAuthModule" type="MyAssembly.Security.MyAuthModule, MyAssembly"/>
...
The Actual HTTP Module:
/// <summary>
/// A module for detecting invalid authentication
/// </summary>
/// <remarks>See "How To Implement IPrincipal" in MSDN</remarks>
public class MyAuthModule : IHttpModule
{
#region IHttpModule Members
void IHttpModule.Dispose() { }
void IHttpModule.Init(HttpApplication context)
{
context.AuthenticateRequest += new EventHandler(context_AuthenticateRequest);
}
#endregion
/// <summary>
/// Inspect the auth request...
/// </summary>
/// <remarks>See "How To Implement IPrincipal" in MSDN</remarks>
private void context_AuthenticateRequest(object sender, EventArgs e)
{
HttpApplication a = (HttpApplication)sender;
HttpContext context = a.Context;
// Extract the forms authentication cookie
string cookieName = FormsAuthentication.FormsCookieName;
HttpCookie authCookie = context.Request.Cookies[cookieName];
if (authCookie != null)
{
FormsAuthenticationTicket authTicket = FormsAuthentication.Decrypt(authCookie.Value);
// check if previously authenticated session is now dead
if (authTicket != null && authTicket.expired)
{
// send them a Response indicating that they've expired.
}
}
}
}
Good luck!
When you log the user, you could drop a cookie on the user's machine indicating that they had a session. When the user reaches the login page (because if their session expired, their login must also have), check to see if the user has that cookie and if they have any of the session keys you expect them to have (if this proves difficult, just set a session variable when they log in). if they have the cookie, but none of the session keys, then their session expired.
I have a main asp.net app, which is written in asp.net 1.1. Runnning underneath the application are several 2.0 apps. To completely logout a user can I just logout of the 1.1 app with FormsAuthentication.SignOut or is it more complicated than that?
What you are looking to do is called Single Sign On and Single Sign Off. There are differences based on how you have the applications set up. I will try to clarify where those differences come into play.
To implement single sign on and single sign off you need to make the cookie name, protection, and path attributes the same between all the applications.
<authentication mode="Forms">
<forms name=".cookiename"
loginUrl="~/Login.aspx"
timeout="30"
path="/" />
</authentication>
Next you need to add the machine keys and they need to be the same between all your applications.
<machineKey validationKey="F9D1A2D3E1D3E2F7B3D9F90FF3965ABDAC304902"
encryptionKey="F9D1A2D3E1D3E2F7B3D9F90FF3965ABDAC304902F8D923AC"
validation="SHA1" />
Are you using second or third level domains for the applications? If so you will need to do a little bit more by adding the domain to the cookie:
protected void Login(string userName, string password)
{
System.Web.HttpCookie cookie = FormsAuthentication.GetAuthCookie(userName, False);
cookie.Domain = "domain1.com";
cookie.Expires = DateTime.Now.AddDays(30);
Response.AppendCookie(cookie);
}
Now to do single sign off, calling FormsAuthentication.SignOut may not be enough. The next best thing is to set the cookie expiration to a past date. This will ensure that the cookie will not be used again for authentication.
protected void Logout(string userName)
{
System.Web.HttpCookie cookie = FormsAuthentication.GetAuthCookie(userName, False);
cookie.Domain = "domain1.com";
cookie.Expires = DateTime.Now.AddDays(-1);
Response.AppendCookie(cookie);
}
I am taking into consideration you are using the same database for all the applications. If the applications use a separate database for registration and authentication, then we will need to do some more. Just let me know if this is the case. Otherwise this should work for you.
It could be easier if you are having a central session store for all your applications. You can then set the session to null in one place.
This worked for me:
In the Logout event, instead of FormsAuthentication.GetAuthCookie method use Cookies collection in Request object as below:
HttpCookie cookie = Request.Cookies.Get(otherSiteCookieName);
cookie.Expires = DateTime.Now.AddDays(-1);
HttpContext.Current.Response.Cookies.Add(cookie);
Ofcourse, this requires u know the Cookie name of the site(s) you want the user to be logged out - which however won't be a problem if you are using the same cookie across all the web apps.
I prefer to use web.config
<authentication mode="Forms">
<forms domain=".tv.loc" loginUrl="~/signin" timeout="2880" name="auth" />
</authentication>