How to track expired WIF fedauth cookies? - asp.net

I have an interesting problem with trying to keep track of expired WIF authentication sessions/cookies.
As a bit of background: the site is MVC 3, uses Windows Identity Foundation (WIF) that has a trust with an ADFS server as an STS. The entire site is protected by SSL. The STS has the token expiry set to 60 minutes.
When a user signs out manually, we just simply call the SignOut method on the FedAuth module:
FederatedAuthentication.WSFederationAuthenticationModule.SignOut(false);
This of course removes the FedAuth cookies, but here's where the problem starts. If I capture those cookies with Fiddler, I can re-present them to the site within their expiry time and still be treated as logged in.
I realise that this is being performed from a privileged position of the browser having accepted fiddler as a proxy... but the customer is worried that those auth cookies not actually being expired presents a significant security risk. They're are not convinced that SSL protects the site sufficiently, and that if an attacker could execute an MITM attack, they could use those cookies after the user thinks they have logged out.
I have explained that if they are vulnerable after log out, they are vulnerable during log in, but they don't care...
So I have looked for ways to be sure that once a user logs off, the fedauth cookies associated with that logon session are treated as expired. The WIF handlers don't seem to have a built in mechanism for tracking expired tokens, and I have not found anything else related to this.
I guess that this is in fact a wider problem -> how to detect expired cookies in general? A valid cookie is a valid cookie!
The obvious solution is to track those cookies after logout somehow, but I'd like to avoid the custom code route if possible; as a noob, a lot of the security literature says to avoid custom coding any kind of session mechanics, as you will probably get it wrong!
Is anyone aware of any standard solutions in ASP.NET to this problem?
Thanks in advance.

You don't without keeping a server-side list of the tokens recently revoked. This is why normally we rely upon an inherent expiration as well as HTTPS to prevent the token from being leaked/stolen.

I was tasked with a similar request by our security team. I opted to store the asp.net session id in the OWIN cookie and on each request that contained a session id in the cookie I verify it matches the active session's Id.
Store session id in the cookie (adapted from this answer) at the end of the first request that is authenticated and doesn't already have the session id in the cookie:
protected override void OnActionExecuted(ActionExecutedContext filterContext)
{
base.OnActionExecuted(filterContext);
bool authenticated = User.Identity.IsAuthenticated;
var sessionGuid = (User as ClaimsPrincipal).FindFirst("sessionID")?.Value;
//put the SessionID into the cookie.
if (authenticated && string.IsNullOrEmpty(sessionGuid))
{
var id= Session.SessionID;
//update the guid claim to track with the session
var authenticationManager = HttpContext.GetOwinContext().Authentication;
// create a new identity from the old one
var identity = new ClaimsIdentity(User.Identity);
// update claim value
identity.RemoveClaim(identity.FindFirst("sessionID"));
identity.AddClaim(new Claim("sessionID", id));
// tell the authentication manager to use this new identity
authenticationManager.AuthenticationResponseGrant =
new AuthenticationResponseGrant(
new ClaimsPrincipal(identity),
new AuthenticationProperties { IsPersistent = true }
);
}
}
Then on each future request if I find a session in the cookie compare it to active session. If they don't match then logout:
protected override void OnActionExecuting( ActionExecutingContext filterContext)
{
var claim = (User as ClaimsPrincipal).FindFirst("sessionID")?.Value;
//does the owin cookie have a sessionID?
if (!string.IsNullOrEmpty(claim))
{
string session = Session.SessionID;
//does it match the one stored in the session?
if(session != claim)
{
//no? log the user out again..
Session.Abandon();
//redirect to logged out page
this.Request.GetOwinContext().Authentication.SignOut();
//tell them its over..
Response.Write("Expired Session");
Response.End();
}
}
base.OnActionExecuting(filterContext);
}

Related

AzureAD and OpenIdConnect session expiration in ASP.net WebForms

I have an ASP.net WebForms application, which I have configured to work with Azure Active Directory and OpenIDConnect based on this article:
http://www.cloudidentity.com/blog/2014/07/24/protecting-an-asp-net-webforms-app-with-openid-connect-and-azure-ad/
Generally speaking, it works fine. But sometimes, after a longer period of inactivity (e.g. filling out a big form), the user is redirected to the main page of the app and his data from the form is lost. This is of course very confusing.
It seems that at some point the session (token?) expires and it needs to re-authenticate. I was able to track a HTTP 302 to https://login.windows.net/... at the point of the redirect. So it would confirm my assumption about re-authentication via AAD.
I cannot exactly tell how often this happens - for me it does not seem to be in regular intervals. I was trying to reproduce the behavior by deleting the cookies AspNet.Cookies and ASP.NET_SessionId but it did not cause the redirect. Interacting with the application just recreated those cookies automatically.
My questions are:
Is there some other location (local storage?) where the information about the login is saved?
How can I (silently, in the background) make sure that the session/token is valid?
In your initialisation code, assuming you've followed the instructions in the article you've linked, there's a line like so,
app.UseCookieAuthentication(new CookieAuthenticationOptions());
When delegating authentication to an Identity Provider, your application still drops some cookies based on the authentication result that control lifetimes. You can tweak these settings to your requirements....e.g.
appBuilder.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType ="Cookies",
ExpireTimeSpan = TimeSpan.FromMinutes(60), // Expire after an hour
SlidingExpiration = true // use sliding expiration..
});
Have a look at the docs for the things you can configure - https://learn.microsoft.com/en-us/previous-versions/aspnet/dn385599(v%3Dvs.113)
Also, an event will be raised whenever it thinks it needs to interact with the Identity Provider, handler optoions available under the Notifications object on OpenIdConnectAuthenticationOptions. One you might be interested in is RedirectToIdentityProvider
private void ConfigureOpenIdConnect()
{
var openIdConnectAuthenticationOptions = new OpenIdConnectAuthenticationOptions
{
// the rest of your settings....then
Notifications =
new OpenIdConnectAuthenticationNotifications
{
RedirectToIdentityProvider =
async context => await RedirectToIdentityProviderHandler(context)
},
UseTokenLifetime = false
};
}
private static Task RedirectToIdentityProviderHandler(
RedirectToIdentityProviderNotification<OpenIdConnectMessage, OpenIdConnectAuthenticationOptions> notification)
{
// do some logging whenever you app is redirecting to the IDP
return Task.FromResult(0);
}
UseTokenLifetime is interesting here, as your identity token has an expiration time and by default the cookie lifetime in the application is the life of the token. If you want to control the cookie lifetime yourself (as per the first code snippet), set this to false here and explicitly control this yourself.
You could use a combination of overriding the identity token lifetime, a longer cookie lifetime, setting sliding expiration to true + some logging on whenever Redirects happen to the Identity Provider.
Obviously though, tweaking settings too much increases attack vector, so consider your security requirements carefully.
As you know, you will get ID token after login successfully via AAD. ID token is Jwt format with an expiration time. You can change the ID token's lifetime with powershell. Please refer to this document https://learn.microsoft.com/en-us/azure/active-directory/develop/active-directory-configurable-token-lifetimes#access-token-lifetime.

.net mvc some users missing cookies

I am using custom forms authentication for an asp.net MVC application, and am having problems with some users seemingly not having cookies. The custom forms authentication method we are using is similar to this - custom forms authentication. Essentially, we create a custom Principal and Identity, serialize it, and store it in the UserData property of the FormsAuthenticationTicket :
Login
MyCustomPrincipal principal = new MyCustomPrincipal(user);
DateTime expiration = DateTime.Now.AddMinutes(30);
FormsAuthenticationTicket authTicket = new FormsAuthenticationTicket(
1,
u.Username,
DateTime.Now,
expiration,
true,
JsonConvert.SerializeObject(principal));
HttpCookie cookie = new HttpCookie(FormsAuthentication.FormsCookieName, FormsAuthentication.Encrypt(authTicket));
cookie.Expires = expiration;
Response.Cookies.Set(cookie);
We then grab the auth cookie in the Application_AuthenticateRequest event of global.asax.
global.asax - Application_AuthenticateRequest
// Get the authentication cookie
string cookieName = FormsAuthentication.FormsCookieName;
HttpCookie authCookie = Context.Request.Cookies[cookieName];
// If the cookie can't be found, don't issue the ticket
if (authCookie == null) return;
// Get the authentication ticket and rebuild the principal
// & identity
FormsAuthenticationTicket authTicket =
FormsAuthentication.Decrypt(authCookie.Value);
MyCustomPrincipal userPrincipal = new MyCustomPrincipal(authTicket.UserData);
DateTime expiration = DateTime.Now.AddMinutes(30);
FormsAuthenticationTicket newAuthTicket = new FormsAuthenticationTicket(
1,
((MyCustomIdentity)userPrincipal.Identity).Username,
DateTime.Now,
expiration,
true,
JsonConvert.SerializeObject(userPrincipal));
authCookie.Value = FormsAuthentication.Encrypt(newAuthTicket);
authCookie.Expires = expiration;
HttpContext.Current.Response.Cookies.Set(authCookie);
Context.User = userPrincipal;
web.config
<authentication mode="Forms">
<forms loginUrl="~/Home/Index" timeout="29" name="MYFORMSAUTH" cookieless="UseCookies"/>
</authentication>
This works fine for the large majority of users, however, there are some users who appear to be getting no authorization cookie set. I did a few tests to add more information to my Elmah error logs to see if I could find out more about the problem.
First, I tried setting some test cookies before and after the setting of the authcookie in the Login method. These cookies did not appear in the Elmah logs, so it appears adding cookies of any kind in this method is not working. However, there are other cookies in the logs, including the ASP.NET_SessionId, a google analytics cookie, and sometimes there are even other cookies I have set at other locations in the application (probably from a previous session)
Second, I tried adding some info to the session from the login action, and including it in the error log if the authcookie was not found on the next action. I included the length of the cookie (the name's length + the encrypted value's length) as well as the time of the attempted login. Both of these are added only if the user's credentials are valid, and the application attempts to add the auth cookie. I do see these values in the error logs being produced. The length is always greater than 0, and I haven't seen any bigger that about 2300, so I don't think size is an issue. And the attempted login is identical to the time that the error occurs - so the session variables should have been set immediately before the error occurred (cookies went missing).
A few more notes -
There doesn't appear to be any browser in particular that seems to be causing the error more (though it is possible it occurs more on mobile browsers)
Again, the site seems to work for the large majority of users, and of course we cannot reproduce the issue
Since I am not seeing the test cookies, I am guessing that for some reason no cookies are being set from the login method at that time (though I can see other cookies set elswhere that would imply previous successful logins)
The http referer in the elmah logs is usually set to the login page, which implies that users are probably not hitting the offending page without logging in (at least some of the time) - the state of the session variables seems to support that assumption
I'm often seeing multiple of these errors in a row (separated by a minute or so) - implying that the issue isn't resolved with repeated login attempts (not sure why it would be)
It appears users who have this issue continue to have the issue. In other words, it doesn't appear to be "luck of the draw" - but something either with the user's account (which the cookie length session variable implies it is serializing correctly), or the client browser.
I've heard of at least one user who was able to log in on a mobile device, but not their desktop
In total the site probably uses 10 or so cookies (including all of the various test cookies that have been added) - before adding the test cookies it used about 4 including the auth cookie. Also, when the bug occurs, there appear to usually only be 2 or 3 cookies in the request, so I don't think number of cookies is an issue.
At this point I'm willing to try almost anything. I tried setting up using the custom identity stored in the session as a backup, but couldn't get that working, so even if anyone has ideas about how to implement that (if possible) it would be appreciated (if this is off topic then I can remove it).
Sorry for the walls of text, but I just wanted to point out all of the potential issues we have investigated and most likely ruled out.
EDIT
It appears there may be another potentially related issue. I'm seeing error logs which lead me to believe that the "IsAuthenticated" attribute of some Identities is being set to false when it should not be. We do initialize this to false, and set it to true after the user answers a security question. When we set it to true, it should update the principle and the authentication ticket / cookie. I'm not sure if this is happening because of some issue with how I am deserializing the custom principal or not.
is server side caching enabled?
is I remember exactly I had similar problem and the cause was the server side caching (misconfigured) and the server side code was not executed but the client reaches the page.
in addition in my side there was a bug (iis bug on caching enabled on dynamic page) that in some situations the session cookie is sent to more than 1 client and this causes unexpected result.
this can explain your non logging behavior and the cookie not present on client.
Regards
So I've sort of given up and decided to use the Session to store my principal, and check it when I don't see the authentication cookie. I can do this somewhat easily by creating a custom Authorize attribute, and checking the session there. I haven't pushed this to production yet, so I'm not 100% sure this will work around the issue, but preliminary testing would suggest it will suffice.
CustomAuthorizeAttribute
public class MyCustomAuthorizeAttribute : AuthorizeAttribute
{
public override void OnAuthorization(AuthorizationContext filterContext)
{
// Get the authentication cookie
string cookieName = FormsAuthentication.FormsCookieName;
HttpCookie authCookie = HttpContext.Current.Request.Cookies[cookieName];
// If the cookie can be found, use the base authentication
if (authCookie != null)
{
base.OnAuthorization(filterContext);
}
else
{
// The cookie is not found, check the session for the principal
var p = HttpContext.Current.Session[FormsAuthentication.FormsCookieName];
if (p != null)
{
// there is a principal object in the session
MyCustomPrincipal principal = (MyCustomPrincipal)p;
HttpContext.Current.User = principal;
// we've loaded the principal, now just do the base authorization
base.OnAuthorization(filterContext);
}
else
{
// there is no principal object in the cookie or the session, the user is not authenticated
HandleUnauthorizedRequest(filterContext);
}
}
}
}
Once we've set the current principal appropriately using the custom authorize attribute, we can then just use the base authorization, so we don't have to worry about implementing that functionality ourselves. The base authorization should check the current principal and authorize based off of that.
I'm not going to mark this as an answer, because it doesn't really solve the underlying issue, but I thought I'd provide it as a potential workaround in case someone else stumbled along a similar issue.

Manually associate session with current request asp.net MVC

I have a MVC 5 asp.net website where I need to expose a number of REST APIs to a stand-alone mobile client. The rest of the site is using Forms based security where it sets the ASP.NET_SessionId as a cookie, and that is used to authenticate the user with the request after they log in. With my mobile application, I am not able to use the cookie method because of the cross-doman issue. What I would like to do is add a header "X-SessionId" with the value of the ASP.NET_SessionId, then on the server side, have a filter that looks for that field, and if it is present, associates the request with the given session. (Client will log in with an AJAX POST call which will return the ASP.NET_SessionId upon successful login).
Is this possible?
Something like this?
public sealed class CustomSecurityAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
if (filterContext == null)
throw new ArgumentNullException("filterContext");
if (string.IsNullOrEmpty(filterContext.HttpContext.Request.Headers["X-SessionId"]) && IsAuthenticated(ilterContext.HttpContext.Request.Headers["X-SessionId"]))
filterContext.Result = new HttpNotFoundResult();
}
private bool IsAuthenticated(string sessionId)
{
// get your user details from your database (or whatever)
var user = new UserRepository().Get(sessionId);
if (user == null)
return false;
// build up an identity, use your own or out of the box.
FormsIdentity itentity = new MyIdentity(user);
// Set the user
filterContext.HttpContext.Current.User = new System.Security.Principal.GenericPrincipal(itentity , user.Roles);
return true;
}
}
You are going to have to store current sessions in your database, so for example when a user logs in grab the sessionid and stick it in the db, so you know they have 1..n current sessions.
Then you can look it up as part of your authentication.
Edit:
Let's take a step back, never mind cookies and sessions for the moment.
You have a website and a restful api, they both servce different purposes and clients and have different security requirements.
So what are the most common options for securing your Api?
Basic authentication.
Most restful APIs require a username/password to be sent through with each request, as part of the headers or in the request itself.
An authentication token
You can provide a token associated with a user account (a guid could suffice) when requests are made you check for the token.
Using an existing protocal like OAuth
I would recommend using these common scenarios to be sure you don't miss something and open your self up to security vulnerabilities.
Is there a reason you can't use any of these?

Is there any good reason why the authentication cookie and the session state cookie are two separate cookies?

Is there any good reason why ASP.NET's session state cookie and the Forms Authentication cookie are two separate cookies? What if I want to "tie" them to each other? Is it possible in an elegant way?
Right now, I am stuck with the following solution, which works, but is still ugly:
[Authorize]
public ActionResult SomeAction(SomeModel model)
{
// The following four lines must be included in *every* controller action
// that requires the user to be authenticated, defeating the purpose of
// having the Authorize attribute.
if (SomeStaticClass.WasSessionStateLost/*?*/) {
FormsAuthentication.SignOut();
return RedirectToAction("Login", "Account");
}
// ...
}
#RPM1984: This is what happens:
[HttpPost]
public ActionResult Login(LoginModel loginModel)
{
if (/* user ok */)
{
// ...
Session["UserID"] = loginModel.UserID;
Session["Password"] = loginModel.Password;
// ...
}
else
{
return View();
}
}
And it doesn't take much guessing to know what WasSessionStateLost does.
Session != Authentication
The session state cookie tracks the user's activity during a browser session.
The forms authentication cookie tracks the user's authenticated activity during a given time period, specified by the expiration date of the ticket and whether or not you have created a persistent cookie (e.g "Remember Me" checkbox).
You shouldn't be touching the session cookie itself, and all it contains is an identifier to tie the client session (browser) to the server.
If you need to access the session, use HttpContext.Current.Session.
What exactly are you trying to "tie" together?
What does SomeStaticClass.WasSessionStateLost do?
I'll start with a solution, then an explanation followed by a recommendation.
Create a custom authorization attribute:
Since your application defines Authorized as follows:
Logged in
Must have values in Session["UserID"] and Session["Password"]
you need to define your own AuthorizationAttribute
public class AuthorizedWithSessionAttribute : AuthorizeAttribute
{
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
if(httpContext.Request.IsAuthenticated &&
Session["UserID"] != null && Session["Password"] != null)
return true;
// sign them out so they can log back in with the Password
if(httpContext.Request.IsAuthenticated)
FormsAuthentication.SignOut();
return false;
}
}
Replace all your [Authorize] attributes with [AuthorizedWithSession] and you shouldn't need to put session check code in your controllers.
I don't know enough about your application, but saving passwords in session (even worse in plain text) is not a secure thing to do.
In addition, as RPM1984 said, the session cookie and authentication cookie are separate.
Explanation:
Think of the session as a bucket of info (on the server side) with your name on it. ASP.NET can take and put stuff in that bucket. ASP.NET gives you a name, your session id, and puts it on the bucket so it can know which one is yours.
The authentication cookie tells ASP.NET that you're authenticated and stores your authentication name in it. The authentication name is usually set by the developer of the application and is usually a unique key (think primary key in a DB) to separate you from the other users.
Recommendation to be more secure:
Encrypt the passwords before your store them. This is not total security, but it beats storing passwords in plain text and of course, if someone were to get a hold of the encryption key, they can crack the passwords.
Rather than using session, which is short lived you could cache in the System.Web.Cache. With this you can add events that are called before an entry is removed and decide accordingly if the cache should be cleared. You can set a higher time-out value on that, with the added bonus that you're not storing the clear text password in a file or database anywhere. Another bonus is you won't be vulnerable to session hijacking.
Of course if the application pool recycles the cache is gone, and as it's in memory load balanced machines will be out of sync, but Velocity or another distributed, out of process cache system would solve that.
It's not perfect though, entries may be dumped due to pressure on the cache, and of course you know this is all a bad idea anyway, so I'll skip that lecture.

how to change session id after login in asp.net

I have a website that's using forms authentication and membership. A user must have cookies enabled to use the site. I've been asked to change the code so that the session id is changed as soon as a user logs in. Aparently this will protect against a Session Fixation attack (http://en.wikipedia.org/wiki/Session_fixation). Does anyone know how I can change the session id without losing the whole session ? PHP has a specific method for doing this but I can't find a .NET equivalent.
Here's a blog post that talks about this:
ASP.NET does not directly support
functionality to regenerate a session
ID. See the documentation regarding
the issue here. There is a not-so
quick and dirty way to do it by
setting the ASPNET_SessionID value to
the empty string and redirecting so
that the value is regenerated.
I have answered a similar question at Generating a new ASP.NET session in the current HTTPContext. Basically we must change some of the SessionStateModule internal state to be able to regenerate session ID without losing objects in the Session. I used reflection to set the _rqId field to the new ID and _rqSessionStateNotFound to true. The downside is we must grant "Full Trust" to the Application.
This is a really old question I'm resurrecting, but here's the solution:
var manager = new SessionIDManager();
bool redirected, isAdded;
manager.SaveSessionID(System.Web.HttpContext.Current,
"5vonjb4mtb1of2fxvhjvkh5d", out redirected, out isAdded);
// sessionId now equals "5vonjb4mtb1of2fxvhjvkh5d"
var sessionId = Session.SessionID;
The method suggested in Microsoft's obsolete KB article 899918 works well even for .NET framework 4.5 Web Forms applications. Here is the archive of the article: https://www.betaarchive.com/wiki/index.php/Microsoft_KB_Archive/899918
How and why session IDs are reused in ASP.NET
Article ID: 899918
Article Last Modified on 9/8/2006
APPLIES TO
Microsoft .NET Framework 1.1
INTRODUCTION
This article describes how and why Microsoft ASP.NET session IDs are used.
MORE INFORMATION
The ASP.NET session state is a technology that lets you store server-side, user-specific data. Web applications can use this data to process requests from the user for which the session state was instantiated. A session state user is identified by a session ID. The session ID is delivered by using one of the following methods:
The session ID is part of a cookie that is sent to the browser of the user.
The session ID is embedded in the URL. This technique is also known as a cookie-less session.
Session IDs are a 120-bit random number that is represented by a 20-character string. The string is formatted so that it can be included in a URL and it does not have to undergo URL encoding. For example, the string may be used in cookie-less sessions. The most commonly used method of delivering session IDs is by using cookies to store the session IDs.
When a user first opens their Web browser and then goes to a Web site that implements ASP.NET session state, a cookie is sent to the browser with the name "ASP.NET_SessionId" and a 20-character value.
When the user browses within the same DNS domain, the Web browser continues to send this cookie to the domain for which it was sourced.
For example, app1.tailspintoys.com and app2.tailspintoys.com are both ASP.NET applications. If the user goes to app1.tailspintoys.com and then goes to app2.tailspintoys.com, both applications would use the same cookie and the same session ID to track the session state of the user within each application. The applications do not share the same session state. The applications only share the session ID.
Therefore, you can reuse session IDs for several reasons. For example, if you reuse session IDs, you do not have to do the following:
Create a new cryptographically unique session ID when you are presented with a valid session ID.
Create a new session ID for every ASP.NET application that is in a single domain.
When the Web application requires a logon and offers a log off page or option, we recommend that you clear the session state when the user has logged off the Web site. To clear the session state, call the Session.Abandon method. The Session.Abandon method lets you flush the session state without waiting for the session state time-out. By default, this time-out is a 20-minute sliding expiration. This expiration is refreshed every time that the user makes a request to the Web site and presents the session ID cookie. The Abandon method sets a flag in the session state object that indicates that the session state should be abandoned. The flag is examined and then acted upon at the end of the page request. Therefore, the user can use session objects within the page after you call the Abandon method. As soon as the page processing is completed, the session is removed.
When you use the in-process session state mode, these session state objects are stored in the HttpCache. The HttpCache supports a callback method when the following conditions are true:
A cache entry is removed.
The Session State Manager registers the Session_OnEnd event handler to be called when the cache entry is removed.
When the Session State Manager removes a session state object that resides in the cache, the HttpCache manager will call any registered callbacks. In effect, this behavior raises the Session_OnEnd event handler.
When you abandon a session, the session ID cookie is not removed from the browser of the user. Therefore, as soon as the session has been abandoned, any new requests to the same application will use the same session ID but will have a new session state instance. At the same time, if the user opens another application within the same DNS domain, the user will not lose their session state after the Abandon method is called from one application.
Sometimes, you may not want to reuse the session ID. If you do and if you understand the ramifications of not reusing the session ID, use the following code example to abandon a session and to clear the session ID cookie:
Session.Abandon();
Response.Cookies.Add(new HttpCookie("ASP.NET_SessionId", ""));
This code example clears the session state from the server and sets the session state cookie to null. The null value effectively clears the cookie from the browser.
When a user does not log off from the application and the session state time-out occurs, the application may still use the same session state cookie if the browser is not closed. This behavior causes the user to be directed to the logon page and the session state cookie of the user to be presented. To guarantee that a new session ID is used when you open the logon page (login.aspx), send a null cookie back to the client. To do this, add a cookie to the response collection. Then, send the response collection back to the client. The easiest way to send a null cookie is by using the Response.Redirect method. Because the cookies collection always has a value for the ASP.NET_SessionId, you cannot just test if this cookie exists because you will create a Response.Redirect loop. You can set a query string on the redirect to the logon page.
Or, as illustrated in the following code example, you can use a different cookie to tell if you are already redirected to the logon page. To help enhance security and to make sure that no one tries to open the logon page by using a second cookie together with the ASP.NET cookie, the following code example uses the FormsAuthentication class to encrypt and decrypt the cookie data. Then, the code example sets a 5-second time-out.
private void Page_Load(object sender, System.EventArgs e)
{
if( !IsPostBack && ( Request.Cookies["__LOGINCOOKIE__"] == null || Request.Cookies["__LOGINCOOKIE__"].Value == "" ) )
{
//At this point, we do not know if the session ID that we have is a new
//session ID or if the session ID was passed by the client.
//Update the session ID.
Session.Abandon();
Response.Cookies.Add(new HttpCookie("ASP.NET_SessionId", ""));
//To make sure that the client clears the session ID cookie, respond to the client to tell
//it that we have responded. To do this, set another cookie.
AddRedirCookie();
Response.Redirect( Request.Path );
}
//Make sure that someone is not trying to spoof.
try
{
FormsAuthenticationTicket ticket =
FormsAuthentication.Decrypt( Request.Cookies["__LOGINCOOKIE__"].Value );
if( ticket == null || ticket.Expired == true )
throw new Exception();
RemoveRedirCookie();
}
catch
{
//If someone is trying to spoof, do it again.
AddRedirCookie();
Response.Redirect( Request.Path );
}
Response.Write("Session.SessionID="+Session.SessionID+"<br/>");
Response.Write("Cookie ASP.NET_SessionId="+Request.Cookies["ASP.NET_SessionId"].Value+"<br/>");
}
private void RemoveRedirCookie()
{
Response.Cookies.Add(new HttpCookie("__LOGINCOOKIE__", ""));
}
private void AddRedirCookie()
{
FormsAuthenticationTicket ticket =
new FormsAuthenticationTicket(1,"Test",DateTime.Now,DateTime.Now.AddSeconds(5), false,"");
string encryptedText = FormsAuthentication.Encrypt( ticket );
Response.Cookies.Add( new HttpCookie( "__LOGINCOOKIE__", encryptedText ) );
}

Resources