23

I have sensitive data that will be hosted on a website, and I would like to prevent the data from being exposed in this scenario:

  • The primary user logs off the application, but does not close the browser (assume a Kiosk environment )

  • Then and a second (malicious) user approaches the terminal and then hits the back button or simply browses the history.

As a result, the malicious user is able to view content they are not authorized for.

  1. What are my options for preventing this?

  2. What can I do to keep the site "user friendly" and allow authenticated users to go back though history, but not unauthenticated/unauthorized ?

Some ideas I was playing with include

  • Cache-Control: no-cache
  • Last-Modified
  • Expires
  • And also Javascript to refresh the authentication for the page

But I'm unsure how these various settings will be affect a Web Browser when operating in an SSL or non SSL mode. This gets even more complicated if a proxy is involved, hence I decided it would be wise to check in with the experts.

makerofthings7
  • 50,090
  • 54
  • 250
  • 536
  • Not sure I really like this solution, but I thought I share it: You could use javascript to do an AJAX call that checks for authentication and then clears the page if it finds the session is invalid, etc. – user606723 Oct 26 '11 at 16:07

6 Answers6

23
  1. Use SSL
  2. Make sure you implement session management correctly - for example the presence of correct session id checked on each page and destroyed with logout
  3. Add cache control: no-cache, pragma: no-cache and expire: -1 headers everywhere
  4. Make sure that forms and every sensitive variable are submitted only through POST requests and not GET (during code audits I've seen countless times pages accepting both methods without any reason)
john
  • 10,968
  • 1
  • 36
  • 43
  • Also regarding point #2 - If you're using WIF then the following code may be needed to [prevent other users from getting session data](http://social.msdn.microsoft.com/Forums/nl-NL/Geneva/thread/57187c9e-4527-4b8d-b20e-f5fbb883a396) – makerofthings7 Oct 29 '11 at 07:00
  • Regarding #1 is using the header `Strict-Transport-Security` a good idea? – makerofthings7 Oct 29 '11 at 07:01
  • Regarding #3: [Some companies prefer to use the current date, or a date in the past](http://security.stackexchange.com/q/8480/396) instead of -1. What is the best solution? – makerofthings7 Oct 29 '11 at 07:34
  • Regarding #4: what's bad with it? Why application shouldn't accept POST requests for urls that are normally operated with POST requests. I asked [this question](http://security.stackexchange.com/questions/8225/should-i-prevent-sending-of-get-requests-for-urls-that-are-operated-with-post-re) previously but it seems that there is no real attack scenario – Andrei Botalov Nov 01 '11 at 22:04
  • @makerofthings: Yes, Strict Transport Security is beneficial, if your site uses SSL sitewide. It prevents downgrade attacks. – D.W. Nov 23 '11 at 03:20
  • @Andrey, is it possible you misread #4? You seem to think #4 is recommending to refuse to accept POST requests, but that's not what #4 is saying. #4 is talking about a different bug, where in some web frameworks, in places that are expecting to receive a POST, the web framework will also allow the server to accept a GET instead of a POST. I agree with john that this is a bug you should avoid. – D.W. Nov 23 '11 at 03:22
  • 1
    AJAX URLs are not stored in the history. I heard that some internet banking sites use AJAX for everything, to avoid leaking information to the history. This also prevents the back button from navigating within the app. – Sam Watkins Jun 02 '15 at 07:33
  • Regarding #2: Back button operation often doesn't reload the URL for POST data. So I think proper session management on server may not help you in that scenario unless you have #3 in place. – Rick Strahl Apr 11 '19 at 20:03
7

If a proxy changes things, then you are not using SSL, and not using SSL is very Bad if the exchanged data is "sensitive". Speaking of which, sensitive data on a public kiosk which can be "enhanced" with a keylogger, well, that's not supremely reassuring either.

You could use Javascript to download and display the content pages without "changing the page" in the browser sense. You would then provide your own back and forward "buttons". Then you add a hook on actual page change (from the browser point of view) to zap internal contents.

None of the above will save you if the user opens a new window and forgets to close the "sensitive" one.

Tom Leek
  • 168,808
  • 28
  • 337
  • 475
6

Cache controls do apply to SSL connections, and are widely supported by browsers so that seems like the best approach. If you are connecting over SSL a proxy will either do a man-in-the-middle and serve up content, or it will just ignore it and route as normal. In the event that it does a MiTM it'll break the browsers certificate trust check so a big red error will show up and (hopefully) your users are taught to step away from the kiosk and use something else. If they don't, well, you are SOL.

If the proxy just routes the connection and you pass in the cache-control or Expired, or Last-Modified headers the browser would hopefully support it. The problem you run into though is the local cache of the HTML page. A malicious user could just read the page from the cache. If the browser doesn't support it, don't let the user view the page. Check the user-agent and compare to a list of known supported versions.

Of course, as Tom said, if the kiosk has a keylogger installed, game over.

Steve
  • 15,155
  • 3
  • 37
  • 66
3

An old question, but adding an answer for modern times.

For sensitive responses, you should set the following header:

Cache-Control: no-store, must-revalidate

These days, you probably don't need to worry about the HTTP 1.0 Expires header. Pragma is only for requests, not responses.

To refresh the page just after session timeout (so that the login form appears), add this header:

Refresh: n + m

Where n is the number of seconds until the session times out and m is a small delay. In Java this is:

session.getMaxInactiveInterval() - ( System.currentTimeMillis() - session.getCreationTime() ) / 1000
Neil McGuigan
  • 3,379
  • 1
  • 16
  • 20
1

From MSFT case #111101555217932

You will see this functionality with any web page that accepts data within an HTTP POST request. The logic of Azure ACS is that the process of submitting forms data through an HTTP POST actually shows up as an entry in your browsers history. If you check your immediate browser history the entry shows up as “Working…” in the list. This allows you to ‘navigate’ back to this entry. Once you attempt to navigate back to this ‘page’ the browser notices that the content has already expired so it prompts you with information that the content has expired and asks if you’d like to resubmit the request to get the ‘updated’ version of that page from the server. Once you click the retry button then this forms data is passed back to ADFS and ADFS treats it as a normal login request so it authenticates that request and sends back the SAML token and automatically redirects you back to your original web page.

You’ll see this same behavior with other browsers as well.

So then we want to know how do you avoid allowing users to log in as another user by simply hitting back, back, back, refresh. I had some discussions with our IE team and our ASP.NET team and we came up with a couple things you could try to avoid this problem.

Client Side Workarounds

  1. One suggestion from our IE team is to modify your ADFS login page so that it doesn’t accept the login credentials directly. Instead it just contains a button that says logon. When the user clicks this logon button then you’d have some javascript that runs window.open() call to launch a second small dialog box that navigates to a second page hosted in your ADFS web site. The second ADFS page will accept the username and password and then submit that for authentication. After the dialog logs in then you can close that window and use the parent window to navigate to your relying party application.

  2. Another suggestion is to try a location.replace() call as described here: Link This would remove an entry from the history list as you navigate to a new page.

Server Side Workarounds

  1. One option is to modify your ADFS login page to also require the user to submit a time based value as part of the form data. Let’s say you added a TimeSubmitted value to the form and when the user clicked the login button you have a script that sets the value to the current time and sends that as part of the login data. Then on the server side you’d check for this value and if it was more than 2 to 5 seconds later than the current time of the server then you could reject the login attempt and send customer back a new fresh login page to manually enter the username/password combination again.

  2. Another server side suggestion is to manipulate the browser’s history to remove that “Working…” entry. To do this you can emit client-side script within the pages PreRender event. So in your login page you could have

     private void WebForm1_PreRender(object sender, System.EventArgs e)
     {
        if (IsPostBack)
     {
     Response.Write("<html><head><script>location.replace('"+Request.Path+"');\n"
     +"</script></head><body></body></html>\n");
     Response.End();
     }
    

The POST request is removed from IE's navigation history. The history will contain only GET requests.

  1. You could also hide the browser navigation bar in your kiosk and just add back and forward buttons to your pages directly. You’d have to handle the page navigation features with your pages themselves and obviously disallow a user to navigate “Back” from the login page of ADFS.

Sadly there is no easy way to work around this issue of disabling someone ability to re-submit form data, even if that form data is used to authenticate and log on a user. Out of all the suggestions proposed I would probably implement the first server side solution where you simply pass in time submitted and if it’s a certain delta away from the server time then just ignore the request because it’s a repost of a previous successful login attempt.

Glorfindel
  • 2,235
  • 6
  • 18
  • 30
makerofthings7
  • 50,090
  • 54
  • 250
  • 536
1

In my experience with php navigation it's "difficult" to back navigate once you are logged off and redirected to the login screen or close the window. Granted any instance of an open window to an open page is an in prior to it expiring. Depending on the number of secured users with secured mouths you have, a variation on CAPTCHA will capture attempts to reuse cookies or memorized keystrokes. The simplest method is to add a predetermined variance to a CAPTCHA pin generator. I.e. the pin# generated is 5463 I add my day of birth 14. In the code for the CAPTCHA you simply add the user string to the if statement. Sorry it's not brilliant but, for the scenario you described it's effective and it's easy.

Z_TLAW
  • 71
  • 4