Click here to Skip to main content
Click here to Skip to main content

Preventing access to folders using RouteExistingFiles property

, 13 Jul 2012
Rate this:
Please Sign up or sign in to vote.
How we can utilize the RouteExistingFiles property to prevent users from accessing unauthorized resources.

RouteExistingFiles Property

When a user request for a static resource like an image, video etc. that is located in a particular folder the ASP.NET happily serves that resource to the user unless we have set some restrictions. Sometimes we need to protect these folders from delivering these resources to users other than the owner. In simple cases we can prevent this through web.config settings but in complex cases like it would be nice if we could control the accessibility through an action/filter and for that we have to direct those requests through MVC pipeline and there comes the RouteExistingFiles property of the RouteCollection class. By setting this property to true we can say MVC to handle those requests instead of giving that responsibility to IIS.

In this article we will see how we can utilize the RouteExistingFiles property to prevent users from accessing unauthorized resources.

Example

Let us say we have a folder that contains private photos of users. We don't want anyone to access the images other than the owner.

Here is our Global.asax.cs:

public static void RegisterRoutes(RouteCollection routes)
{
    routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

    routes.MapRoute(
        "Default",
        "{controller}/{action}/{id}",
        new { controller = "Home", action = "Index", id = UrlParameter.Optional }
    );
}

Let's start by setting the RouteExistingFiles property to true to stop IIS handling those requests and delivering the images directly.

public static void RegisterRoutes(RouteCollection routes)
{
    routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

    routes.RouteExistingFiles = true;
    ...
}

Next thing is, we have to create a route to handle those requests. If you see the URL pattern it resembles the physical path of the resource but that don't need to be the case.

routes.MapRoute(
    "photo",
    "premium/photos/{accountNo}/{image}",
    new { controller = "Photo", action = "Index" }
);

As you guessed we should create a controller and action as follows. The Index action returns the image getting the complete path from the request URL.

public class PhotoController : Controller
{
    public FileResult Index()
    {
        return File(Request.RawUrl, "image/jpeg");
    }
}

Still our action is not safe we have to decorate with a custom authorize attribute. What our custom authorize attribute does is it checks the accountNo passed in the URL with the one stored in the user's session and if they don't matches returns a 401.

public class PhotoAuthorizeAttribute : AuthorizeAttribute
{
    protected override bool AuthorizeCore(HttpContextBase httpContext)
    {
        if (base.AuthorizeCore(httpContext))
        {
            var accountNo = httpContext.Request.RequestContext.RouteData.Values["accountNo"];

            if (accountNo != null && httpContext.Session["AccountNo"].ToString() == accountNo.ToString())
            {
                return true;
            }

            return false;
        }

        return false;
    }
}

[PhotoAuthorize]
public FileResult Index()
{
    return File(Request.RawUrl, "image/jpeg");
}

There is one important problem we notice at-once after setting the RouteExistingFiles to true. The styles and JavaScript files from the Content folder are blocked. That is a problem! this property acts kind of global once we set then it expects all the requests to be passed through the routing module. So how we can deliver the CSS, js? it's bad to create controllers and actions for them. We could do that by using the IgnoreRoute method.

public static void RegisterRoutes(RouteCollection routes)
{
    routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

    routes.IgnoreRoute("Content/{*relpath}");

    routes.RouteExistingFiles = true;

    routes.MapRoute(
        "photo",
        "premium/photos/{accountNo}/{image}",
        new { controller = "Photo", action = "Index" }
    );

    routes.MapRoute(
        "Default",
        "{controller}/{action}/{id}",
        new { controller = "Home", action = "Index", id = UrlParameter.Optional }
    );
}

The IgnoreRoute says the routing module don't handle those requests and note that we have to call that method before setting the RouteExistingFiles.

Avoid caching images by browser

The final problem is browser cache. The browser will cache the images and still an unauthorized user can see the cached version. As a precautionary we have to request the browser don't cache those images and that can be easily achieved using the OutputCache attribute.

[PhotoAuthorize]
[OutputCache(NoStore = true, Duration = 0, VaryByParam = "None")]
public FileResult Index()
{
    return File(Request.RawUrl, "image/jpeg");
}

Summary

So we have seen how we can protect sensitive resources from anonymous/unauthorized users from a little help of the RouteExistingFiles property. Also, when setting the property to true we should use the IgnoreRoute method to avoid handling the requests for static resources like CSS, js by MVC unless there is a need.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

After2050
Software Developer Trigent Software Private Limited
India India
I'm a software developer from south tip of India. I spent most of the time in learning new technologies. I've a keen interest in client-side technologies especially JavaScript and admire it is the most beautiful language ever seen.
 
I like sharing my knowledge and written some non-popular articles. I believe in quality and standards but blames myself for lagging them.
 
I believe in small things and they makes me happy!
Follow on   Twitter

Comments and Discussions

 
-- There are no messages in this forum --
| Advertise | Privacy | Mobile
Web02 | 2.8.140721.1 | Last Updated 13 Jul 2012
Article Copyright 2012 by After2050
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid