Skip to content

Commit 71a9eb7

Browse files
committed
Add section on RFD + whitelist yml/properties/csv
Issue: SPR-13643
1 parent e190f26 commit 71a9eb7

File tree

4 files changed

+105
-48
lines changed

4 files changed

+105
-48
lines changed

spring-web/src/main/java/org/springframework/web/accept/ContentNegotiationManagerFactoryBean.java

+10-4
Original file line numberDiff line numberDiff line change
@@ -80,10 +80,16 @@ public void setFavorPathExtension(boolean favorPathExtension) {
8080
}
8181

8282
/**
83-
* Add mappings from file extensions to media types represented as strings.
84-
* <p>When this mapping is not set or when an extension is not found, the Java
85-
* Action Framework, if available, may be used if enabled via
86-
* {@link #setFavorPathExtension(boolean)}.
83+
* Add a mapping from a key, extracted from a path extension or a query
84+
* parameter, to a MediaType. This is required in order for the parameter
85+
* strategy to work. Any extensions explicitly registered here are also
86+
* whitelisted for the purpose of Reflected File Download attack detection
87+
* (see Spring Framework reference documentation for more details on RFD
88+
* attack protection).
89+
* <p>The path extension strategy will also try to use
90+
* {@link ServletContext#getMimeType} and JAF (if present) to resolve path
91+
* extensions. To change this behavior see the {@link #useJaf} property.
92+
* @param mediaTypes media type mappings
8793
* @see #addMediaType(String, MediaType)
8894
* @see #addMediaTypes(Map)
8995
*/

spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/ContentNegotiationConfigurer.java

+14-6
Original file line numberDiff line numberDiff line change
@@ -62,19 +62,27 @@ public ContentNegotiationConfigurer favorPathExtension(boolean favorPathExtensio
6262
}
6363

6464
/**
65-
* Add mappings from file extensions to media types.
66-
* <p>If this property is not set, the Java Action Framework, if available, may
67-
* still be used in conjunction with {@link #favorPathExtension(boolean)}.
65+
* Add a mapping from a key, extracted from a path extension or a query
66+
* parameter, to a MediaType. This is required in order for the parameter
67+
* strategy to work. Any extensions explicitly registered here are also
68+
* whitelisted for the purpose of Reflected File Download attack detection
69+
* (see Spring Framework reference documentation for more details on RFD
70+
* attack protection).
71+
* <p>The path extension strategy will also try to use
72+
* {@link ServletContext#getMimeType} and JAF (if present) to resolve path
73+
* extensions. To change this behavior see the {@link #useJaf} property.
74+
* @param extension the key to look up
75+
* @param mediaType the media type
76+
* @see #mediaTypes(Map)
77+
* @see #replaceMediaTypes(Map)
6878
*/
6979
public ContentNegotiationConfigurer mediaType(String extension, MediaType mediaType) {
7080
this.mediaTypes.put(extension, mediaType);
7181
return this;
7282
}
7383

7484
/**
75-
* Add mappings from file extensions to media types.
76-
* <p>If this property is not set, the Java Action Framework, if available, may
77-
* still be used in conjunction with {@link #favorPathExtension(boolean)}.
85+
* An alternative to {@link #mediaType} with a Map of registrations to add.
7886
*/
7987
public ContentNegotiationConfigurer mediaTypes(Map<String, MediaType> mediaTypes) {
8088
if (mediaTypes != null) {

spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/AbstractMessageConverterMethodProcessor.java

+3-2
Original file line numberDiff line numberDiff line change
@@ -69,8 +69,9 @@ public abstract class AbstractMessageConverterMethodProcessor extends AbstractMe
6969

7070
/* Extensions associated with the built-in message converters */
7171
private static final Set<String> WHITELISTED_EXTENSIONS = new HashSet<String>(Arrays.asList(
72-
"txt", "text", "json", "xml", "atom", "rss", "png", "jpe", "jpeg", "jpg", "gif", "wbmp", "bmp"));
73-
72+
"txt", "text", "yml", "properties", "csv",
73+
"json", "xml", "atom", "rss",
74+
"png", "jpe", "jpeg", "jpg", "gif", "wbmp", "bmp"));
7475

7576
private final ContentNegotiationManager contentNegotiationManager;
7677

src/asciidoc/index.adoc

+78-36
Original file line numberDiff line numberDiff line change
@@ -30656,23 +30656,74 @@ configuration. For more information on placeholders, see the javadocs of the
3065630656

3065730657

3065830658

30659-
[[mvc-ann-requestmapping-suffix-pattern-match]]
30660-
===== Path Pattern Matching By Suffix
30661-
By default Spring MVC automatically performs `".*"` suffix pattern matching so
30662-
that a controller mapped to `/person` is also implicitly mapped to `/person.*`.
30663-
This allows indicating content types via file extensions, e.g. `/person.pdf`,
30664-
`/person.xml`, etc. A common pitfall however is when the last path segment of the
30665-
mapping is a URI variable, e.g. `/person/{id}`. While a request for `/person/1.json`
30666-
would correctly result in path variable id=1 and extension ".json", when the id
30667-
naturally contains a dot, e.g. `/person/[email protected]` the result does not match
30668-
expectations. Clearly here ".com" is not a file extension.
30659+
==== Suffix Pattern Matching
30660+
By default Spring MVC performs `".{asterisk}"` suffix pattern matching so that a
30661+
controller mapped to `/person` is also implicitly mapped to `/person.{asterisk}`.
30662+
This makes it easy to request different representations of a resource through the
30663+
URL path (e.g. `/person.pdf`, `/person.xml`).
30664+
30665+
Suffix pattern matching can be turned off or restricted to a set of path extensions
30666+
explicitly registered for content negotiation purposes. This is generally
30667+
recommended to minimize ambiguity with common request mappings such as
30668+
`/person/{id}` where a dot might not represent a file extension, e.g.
30669+
`/person/[email protected]` vs `/person/[email protected]`. Furthermore as explained
30670+
in the note below suffix pattern matching as well as content negotiation may be
30671+
used in some circumstances to attempt malicious attacks and there are good
30672+
reasons to restrict them meaningfully.
30673+
30674+
See <<mvc-config-path-matching>> for suffix pattern matching configuration and
30675+
also <<mvc-config-content-negotiation>> for content negotiation configuration.
30676+
30677+
30678+
30679+
[[mvc-ann-requestmapping-rfd]]
30680+
==== Suffix Suffix Pattern Matching and RFD
30681+
30682+
Reflected file download (RFD) attack was first described in a
30683+
https://www.trustwave.com/Resources/SpiderLabs-Blog/Reflected-File-Download---A-New-Web-Attack-Vector/[paper by Trustwave]
30684+
in 2014. The attack is similar to XSS in that it relies on input
30685+
(e.g. query parameter, URI variable) being reflected in the response.
30686+
However instead of inserting JavaScript into HTML, an RFD attack relies on the
30687+
browser switching to perform a download and treating the response as an executable
30688+
script if double-clicked based on the file extension (e.g. .bat, .cmd).
30689+
30690+
In Spring MVC `@ResponseBody` and `ResponseEntity` methods are at risk because
30691+
they can render different content types which clients can request including
30692+
via URL path extensions. Note however that neither disabling suffix pattern matching
30693+
nor disabling the use of path extensions for content negotiation purposes alone
30694+
are effective at preventing RFD attacks.
30695+
30696+
For comprehensive protection against RFD, prior to rendering the response body
30697+
Spring MVC adds a `Content-Disposition:attachment;filename=f.txt` header to
30698+
suggest a fixed and safe download file filename. This is done only if the URL
30699+
path contains a file extension that is neither whitelisted nor explicitly
30700+
registered for content negotiation purposes. However it may potentially have
30701+
side effects when URLs are typed directly into a browser.
30702+
30703+
Many common path extensions are whitelisted by
30704+
default. Furthermore REST API calls are typically not meant to be used as URLs
30705+
directly in browsers. Nevertheless applications that use custom
30706+
`HttpMessageConverter` implementations can explicitly register file extensions
30707+
for content negotiation and the Content-Disposition header will not be added
30708+
for such extensions. See <<mvc-config-content-negotiation>>.
3066930709

30670-
The proper way to address this is to configure Spring MVC to only do suffix pattern
30671-
matching against file extensions registered for content negotiation purposes.
30672-
For more on this, first see <<mvc-config-content-negotiation>> and then
30673-
<<mvc-config-path-matching>> showing how to enable suffix pattern matching
30674-
along with how to use registered suffix patterns only.
30710+
[NOTE]
30711+
====
30712+
This was originally introduced as part of work for
30713+
http://pivotal.io/security/cve-2015-5211[CVE-2015-5211].
30714+
Below are additional recommendations from the report:
3067530715

30716+
* Encode rather than escape JSON responses. This is also an OWASP XSS recommendation.
30717+
For an example of how to do that with Spring see https://github.com/rwinch/spring-jackson-owasp[spring-jackson-owasp].
30718+
* Configure suffix pattern matching to be turned off or restricted to explicitly
30719+
registered suffixes only.
30720+
* Configure content negotiation with the properties “useJaf” and “ignoreUknownPathExtension”
30721+
set to false which would result in a 406 response for URLs with unknown extensions.
30722+
Note however that this may not be an option if URLs are naturally expected to have
30723+
a dot towards the end.
30724+
* Add `X-Content-Type-Options: nosniff` header to responses. Spring Security 4 does
30725+
this by default.
30726+
====
3067630727

3067730728

3067830729
[[mvc-ann-matrix-variables]]
@@ -34208,26 +34259,19 @@ And in XML use the `<mvc:interceptors>` element:
3420834259

3420934260
[[mvc-config-content-negotiation]]
3421034261
==== Content Negotiation
34211-
You can configure how Spring MVC determines the requested media types from the client
34212-
for request mapping as well as for content negotiation purposes. The available options
34213-
are to check the file extension in the request URI, the "Accept" header, a request
34214-
parameter, as well as to fall back on a default content type. By default, file extension
34215-
in the request URI is checked first and the "Accept" header is checked next.
34216-
34217-
For file extensions in the request URI, the MVC Java config and the MVC namespace,
34218-
automatically register extensions such as `.json`, `.xml`, `.rss`, and `.atom` if the
34219-
corresponding dependencies such as Jackson, JAXB2, or Rome are present on the classpath.
34220-
Additional extensions may be not need to be registered explicitly if they can be
34221-
discovered via `ServletContext.getMimeType(String)` or the __Java Activation Framework__
34222-
(see `javax.activation.MimetypesFileTypeMap`). You can register more extensions with the
34223-
{javadoc-baseurl}/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerMapping.html#setUseRegisteredSuffixPatternMatch(boolean)[setUseRegisteredSuffixPatternMatch
34224-
method].
34262+
You can configure how Spring MVC determines the requested media types from the request.
34263+
The available options are to check the URL path for a file extension, check the
34264+
"Accept" header, a specific query parameter, or to fall back on a default content
34265+
type when nothing is requested. By default the path extension in the request URI
34266+
is checked first and the "Accept" header is checked second.
3422534267

34226-
The introduction of `ContentNegotiationManager` also enables selective suffix pattern
34227-
matching for incoming requests. For more details, see its javadocs.
34268+
The MVC Java config and the MVC namespace register `json`, `xml`, `rss`, `atom` by
34269+
default if corresponding dependencies are on the classpath. Additional
34270+
path extension-to-media type mappings may also be registered explicitly and that
34271+
also has the effect of whitelisting them as safe extensions for the purpose of RFD
34272+
attack detection (see <<mvc-ann-requestmapping-rfd>> for more detail).
3422834273

34229-
Below is an example of customizing content negotiation options through the MVC Java
34230-
config:
34274+
Below is an example of customizing content negotiation options through the MVC Java config:
3423134275

3423234276
[source,java,indent=0]
3423334277
[subs="verbatim,quotes"]
@@ -34238,7 +34282,7 @@ config:
3423834282

3423934283
@Override
3424034284
public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
34241-
configurer.favorPathExtension(false).favorParameter(true);
34285+
configurer.mediaType("json", MediaType.APPLICATION_JSON);
3424234286
}
3424334287
}
3424434288
----
@@ -34253,8 +34297,6 @@ that in turn can be created with a `ContentNegotiationManagerFactoryBean`:
3425334297
<mvc:annotation-driven content-negotiation-manager="contentNegotiationManager"/>
3425434298

3425534299
<bean id="contentNegotiationManager" class="org.springframework.web.accept.ContentNegotiationManagerFactoryBean">
34256-
<property name="favorPathExtension" value="false"/>
34257-
<property name="favorParameter" value="true"/>
3425834300
<property name="mediaTypes" >
3425934301
<value>
3426034302
json=application/json

0 commit comments

Comments
 (0)