|
|
6f268b |
commit 5877390a9605f56d9bd6859a54ccbfb16374a78b
|
|
|
6f268b |
Author: Mark Thomas <markt@apache.org>
|
|
|
6f268b |
Date: Wed May 16 14:56:34 2018 +0000
|
|
|
6f268b |
|
|
|
6f268b |
Fix https://bz.apache.org/bugzilla/show_bug.cgi?id=62343
|
|
|
6f268b |
Make CORS filter defaults more secure.
|
|
|
6f268b |
This is the fix for CVE-2018-8014.
|
|
|
6f268b |
|
|
|
6f268b |
git-svn-id: https://svn.apache.org/repos/asf/tomcat/tc7.0.x/trunk@1831730 13f79535-47bb-0310-9956-ffa450edef68
|
|
|
6f268b |
|
|
|
6f268b |
diff -up java/org/apache/catalina/filters/CorsFilter.java.orig java/org/apache/catalina/filters/CorsFilter.java
|
|
|
6f268b |
--- java/org/apache/catalina/filters/CorsFilter.java.orig 2019-02-28 16:37:12.512591576 -0500
|
|
|
6f268b |
+++ java/org/apache/catalina/filters/CorsFilter.java 2019-02-28 16:39:43.449141647 -0500
|
|
|
6f268b |
@@ -260,17 +260,14 @@ public class CorsFilter implements Filte
|
|
|
6f268b |
|
|
|
6f268b |
// Section 6.1.3
|
|
|
6f268b |
// Add a single Access-Control-Allow-Origin header.
|
|
|
6f268b |
- if (anyOriginAllowed && !supportsCredentials) {
|
|
|
6f268b |
- // If resource doesn't support credentials and if any origin is
|
|
|
6f268b |
- // allowed
|
|
|
6f268b |
- // to make CORS request, return header with '*'.
|
|
|
6f268b |
+ if (anyOriginAllowed) {
|
|
|
6f268b |
+ // If any origin is allowed, return header with '*'.
|
|
|
6f268b |
response.addHeader(
|
|
|
6f268b |
CorsFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN,
|
|
|
6f268b |
"*");
|
|
|
6f268b |
} else {
|
|
|
6f268b |
- // If the resource supports credentials add a single
|
|
|
6f268b |
- // Access-Control-Allow-Origin header, with the value of the Origin
|
|
|
6f268b |
- // header as value.
|
|
|
6f268b |
+ // Add a single Access-Control-Allow-Origin header, with the value
|
|
|
6f268b |
+ // of the Origin header as value.
|
|
|
6f268b |
response.addHeader(
|
|
|
6f268b |
CorsFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN,
|
|
|
6f268b |
origin);
|
|
|
6f268b |
@@ -799,6 +796,10 @@ public class CorsFilter implements Filte
|
|
|
6f268b |
.parseBoolean(supportsCredentials);
|
|
|
6f268b |
}
|
|
|
6f268b |
|
|
|
6f268b |
+ if (this.supportsCredentials && this.anyOriginAllowed) {
|
|
|
6f268b |
+ throw new ServletException(sm.getString("corsFilter.invalidSupportsCredentials"));
|
|
|
6f268b |
+ }
|
|
|
6f268b |
+
|
|
|
6f268b |
if (preflightMaxAge != null) {
|
|
|
6f268b |
try {
|
|
|
6f268b |
if (!preflightMaxAge.isEmpty()) {
|
|
|
6f268b |
@@ -1156,7 +1156,7 @@ public class CorsFilter implements Filte
|
|
|
6f268b |
/**
|
|
|
6f268b |
* By default, all origins are allowed to make requests.
|
|
|
6f268b |
*/
|
|
|
6f268b |
- public static final String DEFAULT_ALLOWED_ORIGINS = "*";
|
|
|
6f268b |
+ public static final String DEFAULT_ALLOWED_ORIGINS = "";
|
|
|
6f268b |
|
|
|
6f268b |
/**
|
|
|
6f268b |
* By default, following methods are supported: GET, POST, HEAD and OPTIONS.
|
|
|
6f268b |
@@ -1172,7 +1172,7 @@ public class CorsFilter implements Filte
|
|
|
6f268b |
/**
|
|
|
6f268b |
* By default, support credentials is turned on.
|
|
|
6f268b |
*/
|
|
|
6f268b |
- public static final String DEFAULT_SUPPORTS_CREDENTIALS = "true";
|
|
|
6f268b |
+ public static final String DEFAULT_SUPPORTS_CREDENTIALS = "false";
|
|
|
6f268b |
|
|
|
6f268b |
/**
|
|
|
6f268b |
* By default, following headers are supported:
|
|
|
6f268b |
diff --git a/java/org/apache/catalina/filters/LocalStrings.properties b/java/org/apache/catalina/filters/LocalStrings.properties
|
|
|
6f268b |
index 5c4c792f82..921f101456 100644
|
|
|
6f268b |
--- java/org/apache/catalina/filters/LocalStrings.properties
|
|
|
6f268b |
+++ java/org/apache/catalina/filters/LocalStrings.properties
|
|
|
6f268b |
@@ -14,6 +14,8 @@
|
|
|
6f268b |
# limitations under the License.
|
|
|
6f268b |
|
|
|
6f268b |
addDefaultCharset.unsupportedCharset=Specified character set [{0}] is not supported
|
|
|
6f268b |
+
|
|
|
6f268b |
+corsFilter.invalidSupportsCredentials=It is not allowed to configure supportsCredentials=[true] when allowedOrigins=[*]
|
|
|
6f268b |
corsFilter.invalidPreflightMaxAge=Unable to parse preflightMaxAge
|
|
|
6f268b |
corsFilter.nullRequest=HttpServletRequest object is null
|
|
|
6f268b |
corsFilter.nullRequestType=CORSRequestType object is null
|
|
|
6f268b |
diff --git a/test/org/apache/catalina/filters/TestCorsFilter.java b/test/org/apache/catalina/filters/TestCorsFilter.java
|
|
|
6f268b |
index 06634fbb57..47d520a08e 100644
|
|
|
6f268b |
--- test/org/apache/catalina/filters/TestCorsFilter.java
|
|
|
6f268b |
+++ test/org/apache/catalina/filters/TestCorsFilter.java
|
|
|
6f268b |
@@ -52,8 +52,7 @@ public class TestCorsFilter {
|
|
|
6f268b |
corsFilter.doFilter(request, response, filterChain);
|
|
|
6f268b |
|
|
|
6f268b |
Assert.assertTrue(response.getHeader(
|
|
|
6f268b |
- CorsFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN).equals(
|
|
|
6f268b |
- "https://www.apache.org"));
|
|
|
6f268b |
+ CorsFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN).equals("*"));
|
|
|
6f268b |
Assert.assertTrue(((Boolean) request.getAttribute(
|
|
|
6f268b |
CorsFilter.HTTP_REQUEST_ATTRIBUTE_IS_CORS_REQUEST)).booleanValue());
|
|
|
6f268b |
Assert.assertTrue(request.getAttribute(
|
|
|
6f268b |
@@ -85,8 +84,7 @@ public class TestCorsFilter {
|
|
|
6f268b |
corsFilter.doFilter(request, response, filterChain);
|
|
|
6f268b |
|
|
|
6f268b |
Assert.assertTrue(response.getHeader(
|
|
|
6f268b |
- CorsFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN).equals(
|
|
|
6f268b |
- "https://www.apache.org"));
|
|
|
6f268b |
+ CorsFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN).equals("*"));
|
|
|
6f268b |
Assert.assertTrue(((Boolean) request.getAttribute(
|
|
|
6f268b |
CorsFilter.HTTP_REQUEST_ATTRIBUTE_IS_CORS_REQUEST)).booleanValue());
|
|
|
6f268b |
Assert.assertTrue(request.getAttribute(
|
|
|
6f268b |
@@ -117,8 +115,7 @@ public class TestCorsFilter {
|
|
|
6f268b |
corsFilter.doFilter(request, response, filterChain);
|
|
|
6f268b |
|
|
|
6f268b |
Assert.assertTrue(response.getHeader(
|
|
|
6f268b |
- CorsFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN).equals(
|
|
|
6f268b |
- "https://www.apache.org"));
|
|
|
6f268b |
+ CorsFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN).equals("*"));
|
|
|
6f268b |
Assert.assertTrue(((Boolean) request.getAttribute(
|
|
|
6f268b |
CorsFilter.HTTP_REQUEST_ATTRIBUTE_IS_CORS_REQUEST)).booleanValue());
|
|
|
6f268b |
Assert.assertTrue(request.getAttribute(
|
|
|
6f268b |
@@ -163,41 +160,15 @@ public class TestCorsFilter {
|
|
|
6f268b |
}
|
|
|
6f268b |
|
|
|
6f268b |
/**
|
|
|
6f268b |
- * Tests the presence of the origin (and not '*') in the response, when
|
|
|
6f268b |
- * supports credentials is enabled alongwith any origin, '*'.
|
|
|
6f268b |
+ * Tests the that supports credentials may not be enabled with any origin,
|
|
|
6f268b |
+ * '*'.
|
|
|
6f268b |
*
|
|
|
6f268b |
- * @throws IOException
|
|
|
6f268b |
* @throws ServletException
|
|
|
6f268b |
*/
|
|
|
6f268b |
- @Test
|
|
|
6f268b |
- public void testDoFilterSimpleAnyOriginAndSupportsCredentials()
|
|
|
6f268b |
- throws IOException, ServletException {
|
|
|
6f268b |
- TesterHttpServletRequest request = new TesterHttpServletRequest();
|
|
|
6f268b |
- request.setHeader(CorsFilter.REQUEST_HEADER_ORIGIN,
|
|
|
6f268b |
- TesterFilterConfigs.HTTPS_WWW_APACHE_ORG);
|
|
|
6f268b |
- request.setMethod("GET");
|
|
|
6f268b |
- TesterHttpServletResponse response = new TesterHttpServletResponse();
|
|
|
6f268b |
-
|
|
|
6f268b |
+ @Test(expected=ServletException.class)
|
|
|
6f268b |
+ public void testDoFilterSimpleAnyOriginAndSupportsCredentials() throws ServletException {
|
|
|
6f268b |
CorsFilter corsFilter = new CorsFilter();
|
|
|
6f268b |
- corsFilter.init(TesterFilterConfigs
|
|
|
6f268b |
- .getFilterConfigAnyOriginAndSupportsCredentials());
|
|
|
6f268b |
- corsFilter.doFilter(request, response, filterChain);
|
|
|
6f268b |
-
|
|
|
6f268b |
- Assert.assertTrue(response.getHeader(
|
|
|
6f268b |
- CorsFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN).equals(
|
|
|
6f268b |
- TesterFilterConfigs.HTTPS_WWW_APACHE_ORG));
|
|
|
6f268b |
- Assert.assertTrue(response.getHeader(
|
|
|
6f268b |
- CorsFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_CREDENTIALS)
|
|
|
6f268b |
- .equals(
|
|
|
6f268b |
- "true"));
|
|
|
6f268b |
- Assert.assertTrue(((Boolean) request.getAttribute(
|
|
|
6f268b |
- CorsFilter.HTTP_REQUEST_ATTRIBUTE_IS_CORS_REQUEST)).booleanValue());
|
|
|
6f268b |
- Assert.assertTrue(request.getAttribute(
|
|
|
6f268b |
- CorsFilter.HTTP_REQUEST_ATTRIBUTE_ORIGIN).equals(
|
|
|
6f268b |
- TesterFilterConfigs.HTTPS_WWW_APACHE_ORG));
|
|
|
6f268b |
- Assert.assertTrue(request.getAttribute(
|
|
|
6f268b |
- CorsFilter.HTTP_REQUEST_ATTRIBUTE_REQUEST_TYPE).equals(
|
|
|
6f268b |
- CorsFilter.CORSRequestType.SIMPLE.name().toLowerCase(Locale.ENGLISH)));
|
|
|
6f268b |
+ corsFilter.init(TesterFilterConfigs.getFilterConfigAnyOriginAndSupportsCredentials());
|
|
|
6f268b |
}
|
|
|
6f268b |
|
|
|
6f268b |
/**
|
|
|
6f268b |
@@ -258,8 +229,7 @@ public class TestCorsFilter {
|
|
|
6f268b |
corsFilter.doFilter(request, response, filterChain);
|
|
|
6f268b |
|
|
|
6f268b |
Assert.assertTrue(response.getHeader(
|
|
|
6f268b |
- CorsFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN).equals(
|
|
|
6f268b |
- "https://www.apache.org"));
|
|
|
6f268b |
+ CorsFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN).equals("*"));
|
|
|
6f268b |
Assert.assertTrue(response.getHeader(
|
|
|
6f268b |
CorsFilter.RESPONSE_HEADER_ACCESS_CONTROL_EXPOSE_HEADERS)
|
|
|
6f268b |
.equals(TesterFilterConfigs.EXPOSED_HEADERS));
|
|
|
6f268b |
@@ -707,9 +677,8 @@ public class TestCorsFilter {
|
|
|
6f268b |
corsFilter.init(null);
|
|
|
6f268b |
corsFilter.doFilter(request, response, filterChain);
|
|
|
6f268b |
|
|
|
6f268b |
- Assert.assertTrue(response.getHeader(
|
|
|
6f268b |
- CorsFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN).equals(
|
|
|
6f268b |
- "https://www.apache.org"));
|
|
|
6f268b |
+ Assert.assertNull(response.getHeader(
|
|
|
6f268b |
+ CorsFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN));
|
|
|
6f268b |
Assert.assertTrue(((Boolean) request.getAttribute(
|
|
|
6f268b |
CorsFilter.HTTP_REQUEST_ATTRIBUTE_IS_CORS_REQUEST)).booleanValue());
|
|
|
6f268b |
Assert.assertTrue(request.getAttribute(
|
|
|
6f268b |
@@ -1401,7 +1370,7 @@ public class TestCorsFilter {
|
|
|
6f268b |
Assert.assertTrue(corsFilter.getAllowedOrigins().size() == 0);
|
|
|
6f268b |
Assert.assertTrue(corsFilter.isAnyOriginAllowed());
|
|
|
6f268b |
Assert.assertTrue(corsFilter.getExposedHeaders().size() == 0);
|
|
|
6f268b |
- Assert.assertTrue(corsFilter.isSupportsCredentials());
|
|
|
6f268b |
+ Assert.assertFalse(corsFilter.isSupportsCredentials());
|
|
|
6f268b |
Assert.assertTrue(corsFilter.getPreflightMaxAge() == 1800);
|
|
|
6f268b |
}
|
|
|
6f268b |
|
|
|
6f268b |
@@ -1437,9 +1406,9 @@ public class TestCorsFilter {
|
|
|
6f268b |
Assert.assertTrue(corsFilter.getAllowedHttpHeaders().size() == 6);
|
|
|
6f268b |
Assert.assertTrue(corsFilter.getAllowedHttpMethods().size() == 4);
|
|
|
6f268b |
Assert.assertTrue(corsFilter.getAllowedOrigins().size() == 0);
|
|
|
6f268b |
- Assert.assertTrue(corsFilter.isAnyOriginAllowed());
|
|
|
6f268b |
+ Assert.assertFalse(corsFilter.isAnyOriginAllowed());
|
|
|
6f268b |
Assert.assertTrue(corsFilter.getExposedHeaders().size() == 0);
|
|
|
6f268b |
- Assert.assertTrue(corsFilter.isSupportsCredentials());
|
|
|
6f268b |
+ Assert.assertFalse(corsFilter.isSupportsCredentials());
|
|
|
6f268b |
Assert.assertTrue(corsFilter.getPreflightMaxAge() == 1800);
|
|
|
6f268b |
}
|
|
|
6f268b |
|
|
|
6f268b |
@@ -1543,8 +1512,7 @@ public class TestCorsFilter {
|
|
|
6f268b |
corsFilter.doFilter(request, response, filterChain);
|
|
|
6f268b |
|
|
|
6f268b |
Assert.assertTrue(response.getHeader(
|
|
|
6f268b |
- CorsFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN).equals(
|
|
|
6f268b |
- "https://www.apache.org"));
|
|
|
6f268b |
+ CorsFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN).equals("*"));
|
|
|
6f268b |
Assert.assertNull(request
|
|
|
6f268b |
.getAttribute(CorsFilter.HTTP_REQUEST_ATTRIBUTE_IS_CORS_REQUEST));
|
|
|
6f268b |
Assert.assertNull(request
|
|
|
6f268b |
diff --git a/test/org/apache/catalina/filters/TesterFilterConfigs.java b/test/org/apache/catalina/filters/TesterFilterConfigs.java
|
|
|
6f268b |
index 941d8949d7..cb3d04f813 100644
|
|
|
6f268b |
--- test/org/apache/catalina/filters/TesterFilterConfigs.java
|
|
|
6f268b |
+++ test/org/apache/catalina/filters/TesterFilterConfigs.java
|
|
|
6f268b |
@@ -36,12 +36,13 @@ public class TesterFilterConfigs {
|
|
|
6f268b |
public static final TesterServletContext mockServletContext =
|
|
|
6f268b |
new TesterServletContext();
|
|
|
6f268b |
|
|
|
6f268b |
+ // Default config for the test is to allow any origin
|
|
|
6f268b |
public static FilterConfig getDefaultFilterConfig() {
|
|
|
6f268b |
final String allowedHttpHeaders =
|
|
|
6f268b |
CorsFilter.DEFAULT_ALLOWED_HTTP_HEADERS;
|
|
|
6f268b |
final String allowedHttpMethods =
|
|
|
6f268b |
CorsFilter.DEFAULT_ALLOWED_HTTP_METHODS;
|
|
|
6f268b |
- final String allowedOrigins = CorsFilter.DEFAULT_ALLOWED_ORIGINS;
|
|
|
6f268b |
+ final String allowedOrigins = ANY_ORIGIN;
|
|
|
6f268b |
final String exposedHeaders = CorsFilter.DEFAULT_EXPOSED_HEADERS;
|
|
|
6f268b |
final String supportCredentials =
|
|
|
6f268b |
CorsFilter.DEFAULT_SUPPORTS_CREDENTIALS;
|
|
|
6f268b |
@@ -59,7 +60,7 @@ public class TesterFilterConfigs {
|
|
|
6f268b |
CorsFilter.DEFAULT_ALLOWED_HTTP_HEADERS;
|
|
|
6f268b |
final String allowedHttpMethods =
|
|
|
6f268b |
CorsFilter.DEFAULT_ALLOWED_HTTP_METHODS + ",PUT";
|
|
|
6f268b |
- final String allowedOrigins = CorsFilter.DEFAULT_ALLOWED_ORIGINS;
|
|
|
6f268b |
+ final String allowedOrigins = ANY_ORIGIN;
|
|
|
6f268b |
final String exposedHeaders = CorsFilter.DEFAULT_EXPOSED_HEADERS;
|
|
|
6f268b |
final String supportCredentials = "true";
|
|
|
6f268b |
final String preflightMaxAge =
|
|
|
6f268b |
@@ -77,7 +78,7 @@ public class TesterFilterConfigs {
|
|
|
6f268b |
CorsFilter.DEFAULT_ALLOWED_HTTP_HEADERS;
|
|
|
6f268b |
final String allowedHttpMethods =
|
|
|
6f268b |
CorsFilter.DEFAULT_ALLOWED_HTTP_METHODS + ",PUT";
|
|
|
6f268b |
- final String allowedOrigins = CorsFilter.DEFAULT_ALLOWED_ORIGINS;
|
|
|
6f268b |
+ final String allowedOrigins = ANY_ORIGIN;
|
|
|
6f268b |
final String exposedHeaders = CorsFilter.DEFAULT_EXPOSED_HEADERS;
|
|
|
6f268b |
final String supportCredentials = "false";
|
|
|
6f268b |
final String preflightMaxAge =
|
|
|
6f268b |
@@ -131,7 +132,7 @@ public class TesterFilterConfigs {
|
|
|
6f268b |
CorsFilter.DEFAULT_ALLOWED_HTTP_HEADERS;
|
|
|
6f268b |
final String allowedHttpMethods =
|
|
|
6f268b |
CorsFilter.DEFAULT_ALLOWED_HTTP_METHODS;
|
|
|
6f268b |
- final String allowedOrigins = CorsFilter.DEFAULT_ALLOWED_ORIGINS;
|
|
|
6f268b |
+ final String allowedOrigins = ANY_ORIGIN;
|
|
|
6f268b |
final String exposedHeaders = EXPOSED_HEADERS;
|
|
|
6f268b |
final String supportCredentials =
|
|
|
6f268b |
CorsFilter.DEFAULT_SUPPORTS_CREDENTIALS;
|
|
|
6f268b |
@@ -240,7 +241,7 @@ public class TesterFilterConfigs {
|
|
|
6f268b |
CorsFilter.DEFAULT_ALLOWED_HTTP_HEADERS;
|
|
|
6f268b |
final String allowedHttpMethods =
|
|
|
6f268b |
CorsFilter.DEFAULT_ALLOWED_HTTP_METHODS;
|
|
|
6f268b |
- final String allowedOrigins = CorsFilter.DEFAULT_ALLOWED_ORIGINS;
|
|
|
6f268b |
+ final String allowedOrigins = ANY_ORIGIN;
|
|
|
6f268b |
final String exposedHeaders = CorsFilter.DEFAULT_EXPOSED_HEADERS;
|
|
|
6f268b |
final String supportCredentials =
|
|
|
6f268b |
CorsFilter.DEFAULT_SUPPORTS_CREDENTIALS;
|
|
|
6f268b |
diff -up webapps/docs/changelog.xml.orig webapps/docs/changelog.xml
|
|
|
6f268b |
--- webapps/docs/changelog.xml.orig 2019-02-28 16:33:00.254343407 -0500
|
|
|
6f268b |
+++ webapps/docs/changelog.xml 2019-02-28 16:33:24.135272232 -0500
|
|
|
6f268b |
@@ -78,6 +78,10 @@
|
|
|
6f268b |
NonLoginAuthenticator has to be set for it to work (if no login method
|
|
|
6f268b |
is specified). (remm)
|
|
|
6f268b |
</fix>
|
|
|
6f268b |
+ <fix>
|
|
|
6f268b |
+ <bug>62343</bug>: Make CORS filter defaults more secure. This is the fix
|
|
|
6f268b |
+ for CVE-2018-8014. (markt)
|
|
|
6f268b |
+ </fix>
|
|
|
6f268b |
</changelog>
|
|
|
6f268b |
</subsection>
|
|
|
6f268b |
</section>
|