Skip to content

Adding ResponseCacheAttribute to MVC global filter list does not apply to Razor Pages #18890

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
tiesmaster opened this issue Feb 7, 2020 · 8 comments
Labels
affected-medium This issue impacts approximately half of our customers area-mvc Includes: MVC, Actions and Controllers, Localization, CORS, most templates bug This issue describes a behavior which is not expected - a bug. feature-mvc-execution-pipeline Features related to how MVC executes a controller or razor page feature-razor-pages Priority:1 Work that is critical for the release, but we could probably ship without severity-major This label is used by an internal tool

Comments

@tiesmaster
Copy link

I was about to make this feature request, but quickly searched for a similar issue. I was happy to see that this issue was already reported in #14917. Unfortunately, that issue is already locked down, so therefore I'm raising this issue again.

To answer @pranavkm question:

@sebnilsson is there a reason you are unable to use ResposeCacheAttribute? It's how we expect users to configure the filter on their endpoints.

No, there is not. We've added the attribute, and that works. However, we don't want to use the filter in such a way, since we encountered a security issue in conjunction with Azure FrontDoor (see below). So we would want to turn off caching for all pages. Using the attribute on all pages is tedious, and opens up forgetting this attribute on a page, resulting in a security vulnerability.

We've implemented a page filter now, but would much rather like to use the built-in action filter, since that also validates the cache control parameters. In case we ever would change the caching behavior, we would get validation from the built-in filter, if we misconfigure that.

Additional context: security issue

We're developing an application that authenticates with AzureAD. This uses the standard services.AddAuthentication(AzureADDefaults.AuthenticationScheme).AddAzureAD();, as provided with the New Project template. In front of that, we've added Azure FrontDoor, and when we enabled that, we noticed that we would get someone elses logged in page.

We tracked that down to being related to the caching of Azure FrontDoor: .AddAzureAD() uses the CookieAuthenticationHandler for signing you in. When you then subsequently open a page with the authentication cookie being set, then your page gets cached for everyone, and you see someone elses pages. This is because FrontDoor will cache the first page, of the first user, and when the second user opens up the application, then it will serve the response of the first user.

In case you're wondering why we're using Azure FrontDoor, and how. We're using Azure Front Door primarily for the WAF, and DDos protection features. But it's also nice it's capable of acting as a CDN. So we still want static assets to be cached by FrontDoor. We could disable the caching for now, to circumvent this problem. Thought, you'd have the same problem with a different caching proxy server in front of this. So it seems to me that this needs to be solved in on the side of the webapplication, and not Azure FrontDoor.

@pranavkm
Copy link
Contributor

pranavkm commented Feb 7, 2020

So we would want to turn off caching for all pages.

You could configure the attribute globally for Razor Pages too:

// This applies to Razor Pages and controllers
services.AddRazorPages().AddMvcOptions(o => o.Filters.Add(new ResponseCacheAttribute());

// This configures it only for Razor Pages
services.AddRazorPages(options => options.Conventions.ConfigureFilter(new ResponseCacheAttribute());

It's pretty identical to how you'd configure the ResponseCacheFilter type if it were public.

@tiesmaster
Copy link
Author

Damn, that was quick! I didn't knew you could just use the attribute there :( I actually expected that you could do that, but couldn't figure out how to do that (documentation might need some ❤️, I'll see if I can put in a PR for that).

However, I just tried this, but it didn't work :(

I specifically used this:

services
    .AddRazorPages()
    .AddMvcOptions(options => options.Filters.Add(new ResponseCacheAttribute { NoStore = true, Location = ResponseCacheLocation.None }));

Any idea why this wouldn't work?

@javiercn javiercn added area-mvc Includes: MVC, Actions and Controllers, Localization, CORS, most templates investigate labels Feb 8, 2020
@javiercn javiercn added this to the 5.0.0-preview2 milestone Feb 10, 2020
@tiesmaster
Copy link
Author

@javiercn I wanted to investigate this myself on the latest master branch, but the Functional test of the Mvc solution didn't show up in Visual Studio. Do you know by any chance if that project needs to be run differently than the other unit test projects?

@javiercn
Copy link
Member

@tiesmaster it shouldn't.

I would recommend you invoke ./build.cmd in src/Mvc and then call startvs.cmd to launch vs with the solution opened.

Once you have that, build from inside Visual Studio and it should discover the tests, (if not, it should show something in the Output window under Test)

@pranavkm
Copy link
Contributor

pranavkm commented Feb 10, 2020

@tiesmaster unfortunately this just looks like a bug in the framework. ResponseCacheAttribute typically adds an action filter. For Razor Pages, the provider is meant to configure a page filter instead, but only ever looks at attributes declared on the type:

https://github.com/dotnet/aspnetcore/blob/master/src/Mvc/Mvc.RazorPages/src/ApplicationModels/ResponseCacheFilterApplicationModelProvider.cs#L39

Since this behavior isn't a regression, we would be hard pressed to fix it in a 3.1 patch release. However, we'll look at fixing it in the next major release. In the meanwhile, there's a couple of options:

a) You could copy the filter in to your app and add it. Here are the relevant types:
* https://github.com/dotnet/aspnetcore/blob/release/3.1/src/Mvc/Mvc.Core/src/Filters/ResponseCacheFilterExecutor.cs
* https://github.com/dotnet/aspnetcore/blob/release/3.1/src/Mvc/Mvc.RazorPages/src/Filters/PageResponseCacheFilter.cs

b) You could author an application model provider that executes before the one in the framework to add the ResponseCacheAttribute. Here's what that looks like:

public class MyResponseCachePageApplicationModelProvider : IPageApplicationModelProvider
{
    // Run between the default and ResponseCache application model provider
    public int Order => -1000 + 1;

    public void OnProvidersExecuted(PageApplicationModelProviderContext context)
    {
    }

    public void OnProvidersExecuting(PageApplicationModelProviderContext context)
    {
        var other = context.PageApplicationModel;
        var attributes = other.HandlerTypeAttributes.ToList();
        attributes.Add(new ResponseCacheAttribute { NoStore = true, Location = ResponseCacheLocation.None });
        context.PageApplicationModel = new PageApplicationModel(other.ActionDescriptor, other.DeclaredModelType, other.HandlerType, attributes)
        {
            PageType = other.PageType,
            ModelType = other.ModelType,
        };
    }
}

// Startup
public void ConfigureServices(IServiceCollection services)
{
    ...
    services.AddSingleton<IPageApplicationModelProvider, MyResponseCachePageApplicationModelProvider>();
}

@pranavkm pranavkm added bug This issue describes a behavior which is not expected - a bug. and removed investigate labels Feb 10, 2020
@pranavkm pranavkm changed the title Make "Microsoft.AspNetCore.Mvc.Filters.ResponseCacheFilter" public Adding ResponseCacheAttribute to MVC global filter list does not apply to Razor Pages Feb 10, 2020
@tiesmaster
Copy link
Author

@pranavkm Thanks for the detailed write-up. Too bad it won't be fixed before .NET 5, though, I can related to the policy, you need to make a cut off somewhere. At least, this gives me something to look forward to, in .NET 5 (though, I expect that there will be other things in there I'm looking forward to ;)).

@ghost
Copy link

ghost commented Aug 28, 2020

We've moved this issue to the Backlog milestone. This means that it is not going to be worked on for the coming release. We will reassess the backlog following the current release and consider this item at that time. To learn more about our issue management process and to have better expectation regarding different types of issues you can read our Triage Process.

@pranavkm pranavkm added affected-medium This issue impacts approximately half of our customers severity-major This label is used by an internal tool labels Nov 6, 2020
@javiercn javiercn added feature-mvc-execution-pipeline Features related to how MVC executes a controller or razor page feature-razor-pages labels Apr 18, 2021
@pranavkm pranavkm modified the milestones: Backlog, .NET 7 Planning Oct 19, 2021
@pranavkm pranavkm added the Priority:1 Work that is critical for the release, but we could probably ship without label Oct 28, 2021
@mkArtakMSFT
Copy link
Member

Thanks for contacting us. We're going to address this as part of #37995

@ghost ghost locked as resolved and limited conversation to collaborators Dec 5, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
affected-medium This issue impacts approximately half of our customers area-mvc Includes: MVC, Actions and Controllers, Localization, CORS, most templates bug This issue describes a behavior which is not expected - a bug. feature-mvc-execution-pipeline Features related to how MVC executes a controller or razor page feature-razor-pages Priority:1 Work that is critical for the release, but we could probably ship without severity-major This label is used by an internal tool
Projects
None yet
Development

No branches or pull requests

4 participants