Uploaded image for project: 'Spring Framework'
  1. Spring Framework
  2. SPR-13587

Content-Disposition header causes download in browser for Spring Boot Actuator endpoints

    Details

    • Type: Bug
    • Status: Closed
    • Priority: Minor
    • Resolution: Complete
    • Affects Version/s: 4.1.8, 4.2.2
    • Fix Version/s: 3.2.16, 4.1.9, 4.2.3
    • Component/s: Web
    • Labels:
    • Last commented by a User:
      false

      Description

      The fix to protect against RFD exploits (SPR-13548) introduced a "Content-Disposition:attachment;filename=f.txt" response header for @ResponseBody methods where the URL appears to have an extension that is neither whitelisted by default nor explicitly registered by the application.

      Spring Boot Actuator exposes many endpoints that naturally contain dots and do not represent an extension. When such a URL is typed in a browser it causes content to be downloaded as "f.txt" rather than rendered.

      Several example mappings in Boot:

      /metrics/{name:.*}
      /env/{name:.*}
      /diff/{fromVersion}/{toVersion}
      

      We need to consider ways to make the fix for RFD more flexible with this case in mind (and possible others that might yet be reported), without compromising the security of the application. For once it looks like Spring Boot metrics aren't exposed to RFD since the metric name in the URL has to match a known metric so for example appending a random extension should result in a 404.

      Note this issue was originally reported under Spring Boot ticket #4220.

        Issue Links

          Activity

          Hide
          rstoya05-aop Rossen Stoyanchev added a comment -

          BTW just to be clear ".json" doesn't work as-is. You have to add an explicit mapping with ".json" in it as I showed above or some other mapping with a fixed ending. I see this is more as a way to provide safe access to such URLs in a browser, not something that should dictate REST API design (i.e. putting the horse before the cart.

          Show
          rstoya05-aop Rossen Stoyanchev added a comment - BTW just to be clear ".json" doesn't work as-is. You have to add an explicit mapping with ".json" in it as I showed above or some other mapping with a fixed ending. I see this is more as a way to provide safe access to such URLs in a browser, not something that should dictate REST API design (i.e. putting the horse before the cart.
          Hide
          david_syer Dave Syer added a comment -

          > you should be able to turn suffix pattern matching off there without affecting the rest of the application

          Good point. https://github.com/spring-projects/spring-boot/issues/4402

          Show
          david_syer Dave Syer added a comment - > you should be able to turn suffix pattern matching off there without affecting the rest of the application Good point. https://github.com/spring-projects/spring-boot/issues/4402
          Hide
          david_syer Dave Syer added a comment -

          > ".json" doesn't work as-is. You have to add an explicit mapping with ".json" in it

          This defeats the object of content negotiation being invisible to endpoints doesn't it? We don't want to explicitly map all the Actuator endpoints (and possible future or user provided ones) with all possible media types. The `HttpMessageConverters` are orthogonal and they should decide what content types are supported.

          Show
          david_syer Dave Syer added a comment - > ".json" doesn't work as-is. You have to add an explicit mapping with ".json" in it This defeats the object of content negotiation being invisible to endpoints doesn't it? We don't want to explicitly map all the Actuator endpoints (and possible future or user provided ones) with all possible media types. The `HttpMessageConverters` are orthogonal and they should decide what content types are supported.
          Hide
          rstoya05-aop Rossen Stoyanchev added a comment - - edited

          I didn't suggest changing the existing mappings, rather to create additional ones. Let me try to explain again.

          MetricsMvcEndpoint and EnvironmentMvcEndpoint are mapped to "/{name:.*}". The URLs contain dots and that ends with a Content-Disposition header. However, if the mapping had a fixed ending or an explicit extension the Content-Disposition wouldn't be added. An example of a fixed ending is "/{name:.*}/browser", or perhaps "/metrics" with the metric name becoming a query parameter. An example of an explicitly mapped extension is "/{name:.*}.json".

          My idea is that any one of these would be added as alternatives so they can be used in a browser, for example:

          @RequestMapping(path = {"/{name:.*}", "/{name:.*}.json"}, ...)
          

          Note also that adding a suffix "/" also works (i.e. no Content-Disposition header is added) even today. In any case I'm just raising an idea. Perhaps some sort of a consistent way of entering actuator endpoints in a browser might be one alternative to consider.

          Show
          rstoya05-aop Rossen Stoyanchev added a comment - - edited I didn't suggest changing the existing mappings, rather to create additional ones. Let me try to explain again. MetricsMvcEndpoint and EnvironmentMvcEndpoint are mapped to "/{name:.*}" . The URLs contain dots and that ends with a Content-Disposition header. However, if the mapping had a fixed ending or an explicit extension the Content-Disposition wouldn't be added. An example of a fixed ending is "/{name:.*}/browser" , or perhaps "/metrics" with the metric name becoming a query parameter. An example of an explicitly mapped extension is "/{name:.*}.json" . My idea is that any one of these would be added as alternatives so they can be used in a browser, for example: @RequestMapping (path = { "/{name:.*}" , "/{name:.*}.json" }, ...) Note also that adding a suffix "/" also works (i.e. no Content-Disposition header is added) even today. In any case I'm just raising an idea. Perhaps some sort of a consistent way of entering actuator endpoints in a browser might be one alternative to consider.
          Hide
          rstoya05-aop Rossen Stoyanchev added a comment -

          RFC 6266 defines two disposition types. Switching to "inline" doesn't force the download and that suits our purpose.

          Show
          rstoya05-aop Rossen Stoyanchev added a comment - RFC 6266 defines two disposition types . Switching to "inline" doesn't force the download and that suits our purpose.

            People

            • Assignee:
              rstoya05-aop Rossen Stoyanchev
              Reporter:
              rstoya05-aop Rossen Stoyanchev
              Last updater:
              St├ęphane Nicoll
            • Votes:
              1 Vote for this issue
              Watchers:
              7 Start watching this issue

              Dates

              • Created:
                Updated:
                Resolved:
                Days since last comment:
                2 years, 15 weeks, 2 days ago