Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

In MVC6 how can I block direct access to a folder in wwwroot?

We're developing an application in the latest MVC framework and everything so far has been great. In our application we have decided to embed an angular application in the project under wwwroot/app. I created an app controller and view and prohibited access to the app unless users are authorized. This works great when unauthorized users try to go to localhost/app - it kicks them back to the C# application login page.

I want to take it a step further and also prohibit access to direct files in that folder such as localhost/app/scripts/controllers/name.js or partial html files /app/partials/name-partial.html. In the past I would go into web.config and add the following code but I haven't found the equivalent for the latest framework. Ideally I'd like this to be an entry in startup.cs or appsettings.json if possible

  <location path="app">
    <system.web>
      <authorization>
        <allow roles="User" />
        <deny roles="*" />
      </authorization>
    </system.web>
  </location>
like image 363
chris Avatar asked Jan 08 '16 01:01

chris


2 Answers

After additional research, it seems that the easiest way to accomplish what you want is to actually use the OnPrepareResponse StaticFileOption while configuring UseStaticFiles.

The following would be placed inside of Startup.cs:

public void Configure(IApplicationBuilder app, IHostingEnvironment env, 
    ILoggerFactory loggerFactory)
{
    app.UseStaticFiles(new StaticFileOptions()
    {
        OnPrepareResponse = s =>
        {
            if (s.Context.Request.Path.StartsWithSegments(new PathString("/app")) && 
               !s.Context.User.Identity.IsAuthenticated)
            {
                s.Context.Response.StatusCode = 401;
                s.Context.Response.Body = Stream.Null;
                s.Context.Response.ContentLength = 0;
            }
        }
    });
}

The above code will ONLY be run when executing a static file WITHIN wwwroot and specifically within any path that starts with the "/app" segment. If not authenticated, it rewrites the response so that the normal content is not delivered to the client, along with an appropriate status code.

like image 189
David L Avatar answered Sep 21 '22 21:09

David L


Here's a sightly different approach, that uses an inline middleware to block unauthenticated requests for specific paths:

app.Use((context, next) => {
    // Ignore requests that don't point to static files.
    if (!context.Request.Path.StartsWithSegments("/app")) {
        return next();
    }

    // Don't return a 401 response if the user is already authenticated.
    if (context.User.Identities.Any(identity => identity.IsAuthenticated)) {
        return next();
    }

    // Stop processing the request and return a 401 response.
    context.Response.StatusCode = 401;
    return Task.FromResult(0);
});

Make sure to register it after your authentication middleware (or context.User won't be populated) and before the other middleware (in your case, before the static files middleware). You'll also have to make sure you're using automatic authentication (AutomaticAuthenticate = true). If not, you'll have to use the authentication API:

app.Use(async (context, next) => {
    // Ignore requests that don't point to static files.
    if (!context.Request.Path.StartsWithSegments("/app")) {
        await next();
        return;
    }

    // Don't return a 401 response if the user is already authenticated.
    var principal = await context.Authentication.AuthenticateAsync("Cookies");
    if (principal != null && principal.Identities.Any(identity => identity.IsAuthenticated)) {
        await next();
        return;
    }

    // Stop processing the request and trigger a challenge.
    await context.Authentication.ChallengeAsync("Cookies");
});

Note: if you want to prevent the cookies middleware from replacing the 401 response by a 302 redirect, here's how you can do:

When using Identity (in ConfigureServices):

services.AddIdentity<ApplicationUser, IdentityRole>(options => {
    options.Cookies.ApplicationCookie.Events = new CookieAuthenticationEvents {
        OnValidatePrincipal = options.Cookies.ApplicationCookie.Events.ValidatePrincipal,
        OnRedirectToLogin = context => {
            // When the request doesn't correspond to a static files path,
            // simply apply a 302 status code to redirect the user agent.
            if (!context.Request.Path.StartsWithSegments("/app")) {
                context.Response.Redirect(context.RedirectUri);
            }

            return Task.FromResult(0);
        }
    };
});

When using the cookies middleware without Identity (in Configure):

app.UseCookieAuthentication(options => {
    options.Events = new CookieAuthenticationEvents {
        OnRedirectToLogin = context => {
            // When the request doesn't correspond to a static files path,
            // simply apply a 302 status code to redirect the user agent.
            if (!context.Request.Path.StartsWithSegments("/app")) {
                context.Response.Redirect(context.RedirectUri);
            }

            return Task.FromResult(0);
        }
    };
});
like image 20
Kévin Chalet Avatar answered Sep 21 '22 21:09

Kévin Chalet