ASP.Net Logout not working as expected - asp.net

I know this has been asked and answered many times previously, believe me I've been through all of the posts looking for a solution before asking again.
If a user logs into a page, takes a copy of the URL, logs out then pastes the URL back into the browser, they can get access to the page they had previously visited very briefly before the browser redirects to the login page once more. During this brief window, if they are fast enough with the mouse and can click on a button or other control, they are somehow logged back into the site, no questions asked.
I've tried including the following code suggestion from another thread on the subject into each Page_Load event to avoid caching but no success.
private void ExpirePageCache()
{
Response.Cache.SetCacheability(HttpCacheability.NoCache);
Response.Cache.SetExpires(DateTime.Now - new TimeSpan(1, 0, 0));
Response.Cache.SetLastModified(DateTime.Now);
Response.Cache.SetAllowResponseInBrowserHistory(false);
}
Code from logout.aspx is as follows:
protected void Page_Load(object sender, EventArgs e)
{
FormsAuthentication.SignOut();
HttpContext.Current.Session.Clear();
HttpContext.Current.Session.Abandon();
Response.Redirect("~/Account/Login.aspx");
}
Should I be using Server.Transfer() instead of Response.Redirect()?
I've read somewhere that I'm not allowed to clear the browser history programatically so am a bit stuck. Anyone have any clues please?

Yeah, that line of code is already included in the Page_Load event of the logout.aspx page. It's the first line of code that gets executed...

I suspect something else is up.
When you call response.redirect, none of the page content generated is sent to the client. ASP uses buffering, so as you generate your page, it's buffered until you get to the end, at which point that buffer is sent to the client. This allows you to make changes right up till the last moment, eg sending a redirect response. So that's not your issue.
Are you using output caching or setting the forms auth ticket to persistent? If the browser has a cached copy of the content, it will show that, rather than hit the server (as caching is designed to do). The minute you hit the server though, if the cookie is invalid, then the server should redirect you to somewhere to get a new ticket. if it's not doing that, then somehow it's finding a valid ticket.
You could use Fiddler to monitor the traffic. You can mimic a new browser session by sending request by hand using Fiddler and removing the session & ticket cookies.
Simon

Related

Does Page Unload get called after the response has left IIS?

I'm doing some diagnostic logging in the Page_Unload event in an asp.net application, this logging can take a fair bit of time (about 100ms). Will the response stream get held up by the code in the Page Unload handler? I could do my work asynchronously by using the theadpool but I'd rather not if it won't affect the client's response time.
More information:
#thorkia is correct in that the documentation says that Page_Unload is called after the response is sent to the client, but in my testing (as advised by #steve) it does block. I've tried Casini, IIS Express, Full IIS 7.5 (on a test server) with both release and debug builds, with and without a debugger attached. And, grasping at straws, I tried putting Async=true in the Page Directive. I tried with Fiddler (streaming enabled), and without Fiddler. I've tried with IE9, and Firefox. If the documentation is "correct" then I wonder it it does send the response but perhaps doesn't "finish it off" (what ever that means I'll need to check the HTTP spec) and so the page doesn't render in the browser? But my understanding was that a client browser starts to render the page as it receives the bytes to this doesn't make sense to me either. I've also tried looking at the code in IL Spy but I think this might take me a lot of time.
Now I'm intrigued; am I doing something wrong, or is the documentation misleading?
Why not try it?
protected void Page_UnLoad(object sender, EventArgs e)
{
System.Diagnostics.Debug.WriteLine("In Page_UnLoad");
System.Threading.Thread.Sleep(10000);
System.Diagnostics.Debug.WriteLine("Leaving Page_UnLoad");
}
According to MSDN (http://msdn.microsoft.com/en-us/library/ms178472.aspx) the Page Unload stage is only called after the data has been sent to the client.
Taking a long time to do your logging and clean up will not affect the clients response time for that request but could affect future requests if lots of pages are waiting to be unloaded.

Prevent Postback when user clicks browser's back button

I have a web page that sends email to multiple users (online distribution list). After the submit button is clicked and the email is sent, a status page is shown listing how many emails were sent, errors, and other information. If the user clicks the back button, the email is resent. How can I prevent this?
NOTE: The browser DOES prompt the user to "resubmit" or "resend" data to the page before actually sending the email, but that does not stop my users from clicking it and then wondering why two copies of the email were sent out.
Environment:
Server: C#, ASP.NET 2.0, IIS6
Client: any browser (I don't want an IE-specific solution like SmartNavigation)
In your code, right after the point where e-mails are sent, do a 302 redirect to a confirmation page:
protected void btnSend_Click(object sender, EventArgs e)
{
SendManyEmails();
Response.Redirect("confirmation.aspx");
}
With this kind of code, the POST to the original page will not end up in the browser history.
This common pattern is known as the Post/Redirect/Get pattern.
Bonus info about keeping state when doing Post/Redirect/Get
The main drawback of this pattern is that all state from the handling of the POST request is lost when redirecting the user - thus, commencing a new request context. In ASP.NET this includes members within the Page and all Control objects, as well as everything stored in the ViewState.
If you generate some kind of "status object" - maybe a log of sent mail messages - while handling the POST request, you will need some way to save this object for the following GET request. Some web frameworks has functionality specifically for this: RoR has flash, ASP.NET MVC has TempData. ASP.NET forms has no such concept built in, so you will have to figure something out yourself.
Saving the object to the Session on the POST, reading and deleting it on the following GET would be one way to solve this. You can build an abstraction around this if you use it in several places, or you could search the web for existing implementations of flash/TempData for ASP.NET forms.

How to retain session-state from iframe in a pop-up?

I'm making a web application for a customer that has clients who want to put the login to the app inside of an iframe on their web sites. On a succesful login we want to open the app in a new pop-up window, but it seems that the logged in session is only retained inside of the iframe and not in the main window or in the pop-up. This is only a problem in IE, not in any other browser.
Is there a working way to implement this?
The flow is this:
User goes to client's website (www.url1.com)
User logs in to app, which is in an
iframe (from www.url2.com)
App in iframe validates login
App in iframe uses window.open to
open the app in a new, separate
window
EDIT: Fiddler shows that what happens in the iframe is attached to one iexplore process and what happens in the main window is attached to another. This obviously is the problem, can it be worked around?
Setting cookies in an iframe which loads a page from another domain can cause some strange issues sometimes. And if the cookies don't work, chances are, your login won't work either.
To get around it in a previous case, what I had to do is to add a custom http header in either IIS / Code which suddenly made things work.
Sample C# code:
protected void Application_BeginRequest(Object sender, EventArgs e)
{
HttpContext.Current.Response.AddHeader("p3p", "CP=\"CAO PSA OUR\"");
}
Try to use Fiddler to check if the cookies created by the login page (in the iframe) are sent to the newly opened popup. If not then it maybe a setting in the IE that prevents this from happening.
Edit: To see the cookies in Fiddler go to the Inspectors tab, then to Headers. At Request headers (up) you'll see the cookies sent from server to browser. At Response headers (down) you'll see the cookies sent from browser to the server (they should be sent back to browser for subsequent requests).

Back arrow after signing out

I have an ASP.NET application using Forms Authentication. When the user clicks the Sign Out button on the page it runs the following code.
FormsAuthentication.SignOut();
Response.Expires = 0;
Response.Cache.SetNoStore();
Response.AppendHeader("Pragma", "no-cache");
However the user can still just press the back arrow and see the previous page without needing to log in again. I am sure it has something to do with the previous page being cached. How can I make sure they are prompted to log in again with going back?
Response.Cache.SetCacheability(HttpCacheability.NoCache);
And now you know why you get the message, "You've been logged out. Please close this browser window for security reasons."
No cache is a workaround.
The penultimate workaround is to use ajax to pull any sensitive information down - this would be run again in the back case, and the information should not be cached. It's more connections and more latency, but due to modern browser caching there's not much that can be done except workarounds such as these.
-Adam
While caching is not guaranteed, this works for me for the most part
//Used for disabling page caching
HttpContext.Current.Response.Cache.SetExpires(
DateTime.UtcNow.AddDays(-1));
HttpContext.Current.Response.Cache.SetValidUntilExpires(false);
HttpContext.Current.Response.Cache.SetRevalidation(
HttpCacheRevalidation.AllCaches);
HttpContext.Current.Response.Cache.SetCacheability(
HttpCacheability.NoCache);
HttpContext.Current.Response.Cache.SetNoStore();
Run this in any page's OnInit() method (maybe by using a base class) on any page you don't want the users to be able to cache.
Be careful though, some pages may require you to allow caching such as doing file downloads on SSL pages etc.
I found this code somewhere, if I find the link I'll update the post.

ASP.net: Cache problem when logout

I can't find any post regarding the cache on logout so I'm going to ask this question. If there is a similar question. Please let me know.
I have a Logout page that basically call FormAuthentications signout method then redirect the user to the login page. It work fine but the problem is user can click on the browser's back button and get the cached page. I tried to set the no-cache header on my master page but that doesn't work.
Can someone point me to a article or tutorial or post some tips on how you handle this situation?
thank
Depending on your requirements a possible solution might be to set the Cache-Control header to "no-cache" on every authenticated page. This will prevent pages from being cached downstream. It could be achieved by writing a custom HttpModule that will set the header:
// Prevent the browser from caching the ASPX page
Response.Cache.SetNoStore();
You can also set this in your page's HEAD section by adding the following line of code:
<meta http-equiv="Cache-Control" content="no-cache" />
By doing that if a user clicks the Back button once he's been signed out, he will be redirected to the login form instead of seeing a cached version of his last page which could be a problem if he is using a public computer.
If you're using forms authentication, make sure the forms authentication cookie is deleted when the user logs out. As soon as the user goes to do anything on the cached page (the page they pressed the back button to get to), the site will ask the user to re-login, and then redirect them back to the original page, with fresh data. Viola!
Additionally, regarding caching of pages, you need to set a pretty good number of headers to turn the caching mechanism in the browser and proxy servers off:
"Expires" - set to some date in the past
"Last-Modified" - set to the current date/time
"Cache-Control" - set to "no-cache, must-revalidate"
"Pragma" - set to "no-cache"
That should just about make the page uncacheable. The date/times need to be in RFC1123 format (format specifier "R" in .net e.g. "Mon, 17 Apr 2006 21:22:48 GMT"). You would implement this as:
Response.AddHeader("Expires", new DateTime(1940, 1, 1).ToString("R"));
Response.AddHeader("Last-Modified", DateTime.Now.ToString("R"));
Response.AddHeader("Cache-Control", "no-cache, must-revalidate");
Response.AddHeader("Pragma", "no-cache");
Or something similar, depending on where you want to add all of the headers. I have had good success with this across many browsers and proxy servers, but nothing is fool-proof where page caching is concerned.
There's no foolproof way to accomplish this. The user ultimately has control over the cache settings and no-cache headers can't override these.
Is there a particular concern you are trying to address here (security?), or are you just trying to ensure that users aren't seeing stale data?
This is a hard problem to tackle. You can create a base page and in the constructor you can check that if the person is logged in or not. If the person is not logged in that simply redirect to the login page. This base page will be inherited by all the other ASP.NET pages.
IE6 seems to ignore some cache headers.
Another technique ontop of cache headers would be some clientside javascript to check for the authentication cookie and use history.Forward() if its not there.
Add this to global.asax and it will set the no-cache headers for all pages in the web application. Be sure that disabling caching is really what you want to do however - because caching is a performance benefit.
You can of course, also apply the same Response.Cache commands to pages individually.
This works in FireFox 3, IE7, and somewhat in Opera 9.6. (In Opera, it will work if you don't to any post requests. If you do, the page will still be accessible from the back button the first time, but not afterwards.)
protected void Application_PreSendRequestHeaders(object sender, EventArgs e)
{
if (!Request.Path.Contains("/Content/")) //We WANT images, css, javascripts to be cached!
{
//Otherwise, all of our pages contain sensitive information, and we don't want them cached.
Response.Cache.SetCacheability(HttpCacheability.NoCache);
Response.Cache.SetRevalidation(HttpCacheRevalidation.AllCaches); //for Opera. May only work on https sites
Response.Cache.SetNoStore();
}
}
There is no way you can actually stop is behavior people have removed the back button disabled it but the user would still have controls from short cut for any browser , my suggestion would be that for all those pages where you want the users to be logged in Put them on a POST method i.e. instead of a regular GET use post. What this would do is anytime a user presses the back button for that url the browser would ask the user with an alert box to post the last data cached for this request and there would be a server call where form authentication would work.
It is a bit crude using post where get might work but it solves this issue of yours.

Resources