@@ -846,21 +846,75 @@ configuration. For more information on placeholders, see the javadocs of the
846
846
847
847
848
848
[[mvc-ann-requestmapping-suffix-pattern-match]]
849
- ==== Path Pattern Matching By Suffix
850
- By default Spring MVC automatically performs `".{asterisk}"` suffix pattern matching so
851
- that a controller mapped to `/person` is also implicitly mapped to `/person.{asterisk}`.
852
- This allows indicating content types via file extensions, e.g. `/person.pdf`,
853
- `/person.xml`, etc. A common pitfall however is when the last path segment of the
854
- mapping is a URI variable, e.g. `/person/{id}`. While a request for `/person/1.json`
855
- would correctly result in path variable id=1 and extension ".json", when the id
856
- naturally contains a dot, e.g. `/person/
[email protected] ` the result does not match
857
- expectations. Clearly here ".com" is not a file extension.
849
+ ==== Suffix Pattern Matching
850
+ By default Spring MVC performs `".{asterisk}"` suffix pattern matching so that a
851
+ controller mapped to `/person` is also implicitly mapped to `/person.{asterisk}`.
852
+ This makes it easy to request different representations of a resource through the
853
+ URL path (e.g. `/person.pdf`, `/person.xml`).
854
+
855
+ Suffix pattern matching can be turned off or restricted to a set of path extensions
856
+ explicitly registered for content negotiation purposes. This is generally
857
+ recommended to minimize ambiguity with common request mappings such as
858
+ `/person/{id}` where a dot might not represent a file extension, e.g.
859
+ `/person/
[email protected] ` vs `/person/
[email protected] `. Furthermore as explained
860
+ in the note below suffix pattern matching as well as content negotiation may be
861
+ used in some circumstances to attempt malicious attacks and there are good
862
+ reasons to restrict them meaningfully.
863
+
864
+ See <<mvc-config-path-matching>> for suffix pattern matching configuration and
865
+ also <<mvc-config-content-negotiation>> for content negotiation configuration.
866
+
867
+
868
+
869
+ [[mvc-ann-requestmapping-rfd]]
870
+ ==== Suffix Suffix Pattern Matching and RFD
871
+
872
+ Reflected file download (RFD) attack was first described in a
873
+ https://www.trustwave.com/Resources/SpiderLabs-Blog/Reflected-File-Download---A-New-Web-Attack-Vector/[paper by Trustwave]
874
+ in 2014. The attack is similar to XSS in that it relies on input
875
+ (e.g. query parameter, URI variable) being reflected in the response.
876
+ However instead of inserting JavaScript into HTML, an RFD attack relies on the
877
+ browser switching to perform a download and treating the response as an executable
878
+ script if double-clicked based on the file extension (e.g. .bat, .cmd).
879
+
880
+ In Spring MVC `@ResponseBody` and `ResponseEntity` methods are at risk because
881
+ they can render different content types which clients can request including
882
+ via URL path extensions. Note however that neither disabling suffix pattern matching
883
+ nor disabling the use of path extensions for content negotiation purposes alone
884
+ are effective at preventing RFD attacks.
885
+
886
+ For comprehensive protection against RFD, prior to rendering the response body
887
+ Spring MVC adds a `Content-Disposition:attachment;filename=f.txt` header to
888
+ suggest a fixed and safe download file filename. This is done only if the URL
889
+ path contains a file extension that is neither whitelisted nor explicitly
890
+ registered for content negotiation purposes. However it may potentially have
891
+ side effects when URLs are typed directly into a browser.
892
+
893
+ Many common path extensions are whitelisted by
894
+ default. Furthermore REST API calls are typically not meant to be used as URLs
895
+ directly in browsers. Nevertheless applications that use custom
896
+ `HttpMessageConverter` implementations can explicitly register file extensions
897
+ for content negotiation and the Content-Disposition header will not be added
898
+ for such extensions. See <<mvc-config-content-negotiation>>.
899
+
900
+ [NOTE]
901
+ ====
902
+ This was originally introduced as part of work for
903
+ http://pivotal.io/security/cve-2015-5211[CVE-2015-5211].
904
+ Below are additional recommendations from the report:
905
+
906
+ * Encode rather than escape JSON responses. This is also an OWASP XSS recommendation.
907
+ For an example of how to do that with Spring see https://github.com/rwinch/spring-jackson-owasp[spring-jackson-owasp].
908
+ * Configure suffix pattern matching to be turned off or restricted to explicitly
909
+ registered suffixes only.
910
+ * Configure content negotiation with the properties “useJaf” and “ignoreUknownPathExtension”
911
+ set to false which would result in a 406 response for URLs with unknown extensions.
912
+ Note however that this may not be an option if URLs are naturally expected to have
913
+ a dot towards the end.
914
+ * Add `X-Content-Type-Options: nosniff` header to responses. Spring Security 4 does
915
+ this by default.
916
+ ====
858
917
859
- The proper way to address this is to configure Spring MVC to only do suffix pattern
860
- matching against file extensions registered for content negotiation purposes.
861
- For more on this, first see <<mvc-config-content-negotiation>> and then
862
- <<mvc-config-path-matching>> showing how to enable suffix pattern matching
863
- along with how to use registered suffix patterns only.
864
918
865
919
866
920
@@ -4837,26 +4891,19 @@ And in XML use the `<mvc:interceptors>` element:
4837
4891
4838
4892
[[mvc-config-content-negotiation]]
4839
4893
=== Content Negotiation
4840
- You can configure how Spring MVC determines the requested media types from the client
4841
- for request mapping as well as for content negotiation purposes. The available options
4842
- are to check the file extension in the request URI, the "Accept" header, a request
4843
- parameter, as well as to fall back on a default content type. By default, file extension
4844
- in the request URI is checked first and the "Accept" header is checked next.
4845
-
4846
- For file extensions in the request URI, the MVC Java config and the MVC namespace,
4847
- automatically register extensions such as `.json`, `.xml`, `.rss`, and `.atom` if the
4848
- corresponding dependencies such as Jackson, JAXB2, or Rome are present on the classpath.
4849
- Additional extensions may be not need to be registered explicitly if they can be
4850
- discovered via `ServletContext.getMimeType(String)` or the __Java Activation Framework__
4851
- (see `javax.activation.MimetypesFileTypeMap`). You can register more extensions with the
4852
- {api-spring-framework}/web/servlet/mvc/method/annotation/RequestMappingHandlerMapping.html#setUseRegisteredSuffixPatternMatch(boolean)[setUseRegisteredSuffixPatternMatch
4853
- method].
4854
-
4855
- The introduction of `ContentNegotiationManager` also enables selective suffix pattern
4856
- matching for incoming requests. For more details, see its javadocs.
4857
-
4858
- Below is an example of customizing content negotiation options through the MVC Java
4859
- config:
4894
+ You can configure how Spring MVC determines the requested media types from the request.
4895
+ The available options are to check the URL path for a file extension, check the
4896
+ "Accept" header, a specific query parameter, or to fall back on a default content
4897
+ type when nothing is requested. By default the path extension in the request URI
4898
+ is checked first and the "Accept" header is checked second.
4899
+
4900
+ The MVC Java config and the MVC namespace register `json`, `xml`, `rss`, `atom` by
4901
+ default if corresponding dependencies are on the classpath. Additional
4902
+ path extension-to-media type mappings may also be registered explicitly and that
4903
+ also has the effect of whitelisting them as safe extensions for the purpose of RFD
4904
+ attack detection (see <<mvc-ann-requestmapping-rfd>> for more detail).
4905
+
4906
+ Below is an example of customizing content negotiation options through the MVC Java config:
4860
4907
4861
4908
[source,java,indent=0]
4862
4909
[subs="verbatim,quotes"]
@@ -4867,7 +4914,7 @@ config:
4867
4914
4868
4915
@Override
4869
4916
public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
4870
- configurer.favorPathExtension(false).favorParameter(true );
4917
+ configurer.mediaType("json", MediaType.APPLICATION_JSON );
4871
4918
}
4872
4919
}
4873
4920
----
@@ -4882,8 +4929,6 @@ that in turn can be created with a `ContentNegotiationManagerFactoryBean`:
4882
4929
<mvc:annotation-driven content-negotiation-manager="contentNegotiationManager"/>
4883
4930
4884
4931
<bean id="contentNegotiationManager" class="org.springframework.web.accept.ContentNegotiationManagerFactoryBean">
4885
- <property name="favorPathExtension" value="false"/>
4886
- <property name="favorParameter" value="true"/>
4887
4932
<property name="mediaTypes">
4888
4933
<value>
4889
4934
json=application/json
0 commit comments