How To Prevent the Back Button after Logout in ASP.NET Core MVC

How To Prevent the Back Button after Logout in ASP.NET Core MVC

If you have a web application that is secured by a login system, you probably don’t want any restricted content to be available after a user signs out. Suppose a user accesses your web application from a shared computer, and after signing out (or timing out) the user does not close the browser. Anyone who comes along later can simply use the back button to find interesting information that is in the browser’s cache. That’s definitely a security problem.

The solution is to prevent the browser from caching content that requires authentication. This is done through the Cache-Control HTTP Response header, which instructs the browser what it is allowed to do with a page. If you return the Cache-Control header with a value of no-cache, the browser will send a new request to the server every time the user navigates to the page. However, if someone uses the back button to go back to the page, many browsers will not re-request the page. They will re-display the page from the cache. So the no-cache value in the the header doesn’t actually prevent the browser for caching the response! So that won’t fix our security hole.

If you really want to prevent the browser from keeping a copy of the page, you must add no-store to the header. You can use the browser’s developer tools to examine the response headers that the server is returning. Any pages that require authentication should have a header that looks like this:

Cache-Control: no-store,no-cache

With that header set, the browser will not store the page. If the back button is pressed, the browser is forced to re-request the page. If the user has already signed out, the new request will cause the user to be redirected to sign in rather than revealing restricted content.

In ASP.NET Core you can use a ResponseCache attribute to set the Cache-Control header. The ResponseCache attribute is often used to cache content, but it can also be used to prevent caching.

Here is an example:

[Authorize]
[ResponseCache(Location = ResponseCacheLocation.None, NoStore = true)]
public IActionResult RestrictedContent()
{
    return View();
}

If you want to add no-store to every page in your application, you can ad this snippet to the ConfigureServices method in Startup.cs:

services.Configure(options =>
{
    options.CacheProfiles.Add("DefaultNoCacheProfile", new CacheProfile
    {
        NoStore = true,
        Location = ResponseCacheLocation.None
    });
    options.Filters.Add(new ResponseCacheAttribute
    {
        CacheProfileName = "DefaultNoCacheProfile"                    
    });
});

Now your pages should be protected. However, at the moment there is a design flaw in the ASP.NET Core anti-forgery code that can cause your Cache-Control header header to be overwritten. So be sure to test your application and examine the response headers to ensure that every page is actually secure.

Leave a Reply

Your email address will not be published. Required fields are marked *