Tomcat serving static content

17,959

Solution 1

There are several better ways to serve static content.

The traditional approach was to use a UrlRewriteFilter to remap URLs as follows:

web.xml:

<filter>
    <filter-name>UrlRewriteFilter</filter-name>
    <filter-class>org.tuckey.web.filters.urlrewrite.UrlRewriteFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>UrlRewriteFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>
...
<servlet-mapping>
    <servlet-name>Spring MVC Dispatcher Servlet</servlet-name>
    <url-pattern>/app/*</url-pattern>
</servlet-mapping>

urlrewrite.xml:

<urlrewrite default-match-type="wildcard">
    <rule>
        <from>/images/**</from>
        <to>/images/$1</to>
    </rule>
    <rule>
        <from>/scripts/**</from>
        <to>/scripts/$1</to>
    </rule>
    <rule>
        <from>/styles/**</from>
        <to>/styles/$1</to>
    </rule>
    <rule>
        <from>/**</from>
        <to>/app/$1</to>
    </rule>
</urlrewrite>

This approach can be seen in the most of Spring samples.


Spring 3.0.1 introduced a newer apporach - it can serve static content via DispatcherServlet. It can be configured using <mvc:resource> element in Spring's config file. In Spring 3.0.4 it was extended with support of multiple location and cache control options, see 15.12.4 mvc:resources.

Solution 2

Did you test it? The /META-INF and /WEB-INF folders are supposed to be public inaccessible as per the Servlet specification. The client should have gotten a 404 for this. It would otherwise have been a bug in DefaultServlet.

Here's an extract of Servlet 2.5 spec:

SRV.9.5 Directory Structure

... Also, any requests from the client to access the resources in WEB-INF/ directory must be returned with a SC_NOT_FOUND(404) response.

and

SRV.9.6 Web Application Archive File

... Also, any requests to access the resources in META-INF directory must be returned with a SC_NOT_FOUND(404) response.


Update: OK, I take my words back. I can reproduce this on latest Tomcat 6 and 7. It's definitely a bug in DefaultServlet. It works fine (returns 404) on Glassfish 3.0.1.


Update 2: I've reported this to the Tomcat guys as issue 50026.


Update 3: one of the Tomcat guys responsed as:

I'm thinking this is a WONTFIX.

The servlet engine protects the WEB-INF and META-INF paths in the web application (which is working fine), not files of that name under arbitrary paths.

What's actually happening here is you're configuring a general purpose file serving servlet to mount up your entire web application under a different path - it's equivalent to configuring Apache to do the same thing. Except that DefaultServlet isn't a general purpose file server - it's designed to be mapped to /, and you can't configure it to do anything but serve files out of the web application directory.

I'm guessing you're trying to work around a problem introduced by mapping another servlet to /*, which is basically trying to work around the way a servlet engine works. How to access static resources when mapping a global front controller servlet on /* has an example of a better way to approach things if this is what you're trying to do.

Advice to remount DefaultServlet in Tomcat seems to have been around as long as Tomcat has existed, so perhaps we need to lock it down (so people can't accidentally create insecure configurations) or support mounting specific directories (inside or outside the web application), and break if accessing the root resources when mapped to a sub-path in any case.


Update 4: they've ultimately fixed it:

Fixes for DefaultServlet and WebdavServlet are committed for 7.0.x (will be in 7.0.4+) and proposed for 6.0.x. Will need to check 5.5.x and see if a backport is required for that too.

Solution 3

WEB-INF and META-INF are private folders. The client must be getting 404 error for this. As META_INF contents are not accessible directly.Otherwise move all sensitive files in WEB_INF.

Share:
17,959
Steve
Author by

Steve

Updated on July 03, 2022

Comments

  • Steve
    Steve almost 2 years

    I have a Spring app and I'm wondering the best way to serve static content. I have tried the following:

    <servlet-mapping>
        <servlet-name>default</servlet-name>
        <url-pattern>/static/*</url-pattern>
    </servlet-mapping>
    <servlet-mapping>
        <servlet-name>app</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
    

    This works, but the behaviour of the DefaultServlet means that any request of the form /static/PATH serves the file from webapp/PATH. This exposes a massive vulnerability, allowing sensitive information to be shown with URLs such as: http://localhost/app/static/META-INF/context.xml

    What's the common solution for this? Should I move the sensitive files? Write my own DefaultServlet? Or is there a better way to serve static content?

  • Steve
    Steve over 13 years
    I did test it and I can see my context.xml clearly, with my database passwords inside.
  • BalusC
    BalusC over 13 years
    Either you've placed the WEB-INF and META-INF folders in the wrong location (they must be placed in webcontent root, not in some subfolder like /static), or there's a bug in Tomcat version used.
  • axtavt
    axtavt over 13 years
    Tomcat 6 actually works so. Even Spring's Petclinic sample is vulnerable src.springframework.org/svn/spring-samples/petclinic/trunk Wow, just wow.
  • Steve
    Steve over 13 years
    Just got a fresh Tomcat 6, a brand new Dynamic Web Project in eclipse and added just the following to web.xml and I can access META-INF: <servlet-mapping> <servlet-name>default</servlet-name> <url-pattern>/static/*</url-pattern> </servlet-mapping>
  • BalusC
    BalusC over 13 years
    I take my words back, I can reproduce this. This is astonishing.
  • axtavt
    axtavt over 13 years
    @BalusC: Both described approaches solve this problem - the former by remapping /* to /app/* with explicit exclusions, the latter - by serving static content via DispatcherServlet mapped to /.
  • Juan Calero
    Juan Calero almost 12 years
    In my experience, Spring 3.0.4 is needed to recognize the <mvc:resource> tag (Not 3.0.1)