VYPR
Critical severityNVD Advisory· Published May 16, 2018· Updated Aug 5, 2024

CVE-2018-8014

CVE-2018-8014

Description

The defaults settings for the CORS filter provided in Apache Tomcat 9.0.0.M1 to 9.0.8, 8.5.0 to 8.5.31, 8.0.0.RC1 to 8.0.52, 7.0.41 to 7.0.88 are insecure and enable 'supportsCredentials' for all origins. It is expected that users of the CORS filter will have configured it appropriately for their environment rather than using it in the default configuration. Therefore, it is expected that most users will not be impacted by this issue.

AI Insight

LLM-synthesized narrative grounded in this CVE's description and references.

Default CORS filter in Apache Tomcat enables supportsCredentials for all origins, insecure.

Vulnerability

The CORS filter in Apache Tomcat has insecure default settings. In versions 9.0.0.M1 to 9.0.8, 8.5.0 to 8.5.31, 8.0.0.RC1 to 8.0.52, and 7.0.41 to 7.0.88, the filter's default configuration enables supportsCredentials for all origins. This means that by default, the filter allows any origin to make cross-origin requests with credentials (such as cookies and HTTP authentication). [1][3][4]

Exploitation

An attacker can craft a malicious website that sends a cross-origin request to a vulnerable Tomcat server. Since the CORS filter's default configuration trusts any origin with credentials, the browser will send the target site's cookies along with the request. This allows the attacker to read the response and potentially perform actions on behalf of the logged-in user if the server does not validate the origin. No authentication is required beyond the victim being logged into the Tomcat application. [1]

Impact

Successful exploitation can lead to unauthorized access to sensitive data or actions as the victim user. An attacker can read responses that may contain private information or perform operations on the user's behalf, leading to information disclosure or privilege escalation. [1]

Mitigation

Users should upgrade to Tomcat 9.0.9, 8.5.32, 8.0.53, or 7.0.89 or later, where the default configuration is corrected. For those unable to upgrade, the supportsCredentials parameter in the CORS filter configuration should be explicitly set to false for untrusted origins. Note that Tomcat 8.0.x and 8.5.x are now end-of-life and no longer receive updates; users are encouraged to migrate to 9.0.x. [1][3][4]

AI Insight generated on May 22, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
org.apache.tomcat.embed:tomcat-embed-coreMaven
>= 9.0.0.M1, < 9.0.99.0.9
org.apache.tomcat.embed:tomcat-embed-coreMaven
>= 8.5.0, < 8.5.328.5.32
org.apache.tomcat.embed:tomcat-embed-coreMaven
>= 8.0.0RC1, < 8.0.538.0.53
org.apache.tomcat.embed:tomcat-embed-coreMaven
>= 7.0.41, < 7.0.887.0.88

Affected products

35

Patches

4
5877390a9605

Fix https://bz.apache.org/bugzilla/show_bug.cgi?id=62343

https://github.com/apache/tomcatMark ThomasMay 16, 2018via ghsa
5 files changed · +37 61
  • java/org/apache/catalina/filters/CorsFilter.java+10 9 modified
    @@ -278,17 +278,14 @@ protected void handleSimpleCORS(final HttpServletRequest request,
     
             // Section 6.1.3
             // Add a single Access-Control-Allow-Origin header.
    -        if (anyOriginAllowed && !supportsCredentials) {
    -            // If resource doesn't support credentials and if any origin is
    -            // allowed
    -            // to make CORS request, return header with '*'.
    +        if (anyOriginAllowed) {
    +            // If any origin is allowed, return header with '*'.
                 response.addHeader(
                         CorsFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN,
                         "*");
             } else {
    -            // If the resource supports credentials add a single
    -            // Access-Control-Allow-Origin header, with the value of the Origin
    -            // header as value.
    +            // Add a single Access-Control-Allow-Origin header, with the value
    +            // of the Origin header as value.
                 response.addHeader(
                         CorsFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN,
                         origin);
    @@ -804,6 +801,10 @@ private void parseAndStore(final String allowedOrigins,
             // For any value other then 'true' this will be false.
             this.supportsCredentials = Boolean.parseBoolean(supportsCredentials);
     
    +        if (this.supportsCredentials && this.anyOriginAllowed) {
    +            throw new ServletException(sm.getString("corsFilter.invalidSupportsCredentials"));
    +        }
    +
             try {
                 if (!preflightMaxAge.isEmpty()) {
                     this.preflightMaxAge = Long.parseLong(preflightMaxAge);
    @@ -1157,7 +1158,7 @@ protected enum CORSRequestType {
         /**
          * By default, all origins are allowed to make requests.
          */
    -    public static final String DEFAULT_ALLOWED_ORIGINS = "*";
    +    public static final String DEFAULT_ALLOWED_ORIGINS = "";
     
         /**
          * By default, following methods are supported: GET, POST, HEAD and OPTIONS.
    @@ -1173,7 +1174,7 @@ protected enum CORSRequestType {
         /**
          * By default, support credentials is turned on.
          */
    -    public static final String DEFAULT_SUPPORTS_CREDENTIALS = "true";
    +    public static final String DEFAULT_SUPPORTS_CREDENTIALS = "false";
     
         /**
          * By default, following headers are supported:
    
  • java/org/apache/catalina/filters/LocalStrings.properties+2 0 modified
    @@ -14,6 +14,8 @@
     # limitations under the License.
     
     addDefaultCharset.unsupportedCharset=Specified character set [{0}] is not supported
    +
    +corsFilter.invalidSupportsCredentials=It is not allowed to configure supportsCredentials=[true] when allowedOrigins=[*]
     corsFilter.invalidPreflightMaxAge=Unable to parse preflightMaxAge
     corsFilter.nullRequest=HttpServletRequest object is null
     corsFilter.nullRequestType=CORSRequestType object is null
    
  • test/org/apache/catalina/filters/TestCorsFilter.java+15 47 modified
    @@ -52,8 +52,7 @@ public void testDoFilterSimpleGET() throws IOException, ServletException {
             corsFilter.doFilter(request, response, filterChain);
     
             Assert.assertTrue(response.getHeader(
    -                CorsFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN).equals(
    -                "https://www.apache.org"));
    +                CorsFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN).equals("*"));
             Assert.assertTrue(((Boolean) request.getAttribute(
                     CorsFilter.HTTP_REQUEST_ATTRIBUTE_IS_CORS_REQUEST)).booleanValue());
             Assert.assertTrue(request.getAttribute(
    @@ -85,8 +84,7 @@ public void testDoFilterSimplePOST() throws IOException, ServletException {
             corsFilter.doFilter(request, response, filterChain);
     
             Assert.assertTrue(response.getHeader(
    -                CorsFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN).equals(
    -                "https://www.apache.org"));
    +                CorsFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN).equals("*"));
             Assert.assertTrue(((Boolean) request.getAttribute(
                     CorsFilter.HTTP_REQUEST_ATTRIBUTE_IS_CORS_REQUEST)).booleanValue());
             Assert.assertTrue(request.getAttribute(
    @@ -117,8 +115,7 @@ public void testDoFilterSimpleHEAD() throws IOException, ServletException {
             corsFilter.doFilter(request, response, filterChain);
     
             Assert.assertTrue(response.getHeader(
    -                CorsFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN).equals(
    -                "https://www.apache.org"));
    +                CorsFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN).equals("*"));
             Assert.assertTrue(((Boolean) request.getAttribute(
                     CorsFilter.HTTP_REQUEST_ATTRIBUTE_IS_CORS_REQUEST)).booleanValue());
             Assert.assertTrue(request.getAttribute(
    @@ -163,41 +160,15 @@ public void testDoFilterSimpleSpecificHeader() throws IOException,
         }
     
         /**
    -     * Tests the presence of the origin (and not '*') in the response, when
    -     * supports credentials is enabled alongwith any origin, '*'.
    +     * Tests the that supports credentials may not be enabled with any origin,
    +     * '*'.
          *
    -     * @throws IOException
          * @throws ServletException
          */
    -    @Test
    -    public void testDoFilterSimpleAnyOriginAndSupportsCredentials()
    -            throws IOException, ServletException {
    -        TesterHttpServletRequest request = new TesterHttpServletRequest();
    -        request.setHeader(CorsFilter.REQUEST_HEADER_ORIGIN,
    -                TesterFilterConfigs.HTTPS_WWW_APACHE_ORG);
    -        request.setMethod("GET");
    -        TesterHttpServletResponse response = new TesterHttpServletResponse();
    -
    +    @Test(expected=ServletException.class)
    +    public void testDoFilterSimpleAnyOriginAndSupportsCredentials() throws ServletException {
             CorsFilter corsFilter = new CorsFilter();
    -        corsFilter.init(TesterFilterConfigs
    -                .getFilterConfigAnyOriginAndSupportsCredentials());
    -        corsFilter.doFilter(request, response, filterChain);
    -
    -        Assert.assertTrue(response.getHeader(
    -                CorsFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN).equals(
    -                TesterFilterConfigs.HTTPS_WWW_APACHE_ORG));
    -        Assert.assertTrue(response.getHeader(
    -                CorsFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_CREDENTIALS)
    -                .equals(
    -                        "true"));
    -        Assert.assertTrue(((Boolean) request.getAttribute(
    -                CorsFilter.HTTP_REQUEST_ATTRIBUTE_IS_CORS_REQUEST)).booleanValue());
    -        Assert.assertTrue(request.getAttribute(
    -                CorsFilter.HTTP_REQUEST_ATTRIBUTE_ORIGIN).equals(
    -                TesterFilterConfigs.HTTPS_WWW_APACHE_ORG));
    -        Assert.assertTrue(request.getAttribute(
    -                CorsFilter.HTTP_REQUEST_ATTRIBUTE_REQUEST_TYPE).equals(
    -                CorsFilter.CORSRequestType.SIMPLE.name().toLowerCase(Locale.ENGLISH)));
    +        corsFilter.init(TesterFilterConfigs.getFilterConfigAnyOriginAndSupportsCredentials());
         }
     
         /**
    @@ -258,8 +229,7 @@ public void testDoFilterSimpleWithExposedHeaders() throws IOException,
             corsFilter.doFilter(request, response, filterChain);
     
             Assert.assertTrue(response.getHeader(
    -                CorsFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN).equals(
    -                "https://www.apache.org"));
    +                CorsFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN).equals("*"));
             Assert.assertTrue(response.getHeader(
                     CorsFilter.RESPONSE_HEADER_ACCESS_CONTROL_EXPOSE_HEADERS)
                     .equals(TesterFilterConfigs.EXPOSED_HEADERS));
    @@ -707,9 +677,8 @@ public void testInitDefaultFilterConfig() throws IOException,
             corsFilter.init(null);
             corsFilter.doFilter(request, response, filterChain);
     
    -        Assert.assertTrue(response.getHeader(
    -                CorsFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN).equals(
    -                "https://www.apache.org"));
    +        Assert.assertNull(response.getHeader(
    +                CorsFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN));
             Assert.assertTrue(((Boolean) request.getAttribute(
                     CorsFilter.HTTP_REQUEST_ATTRIBUTE_IS_CORS_REQUEST)).booleanValue());
             Assert.assertTrue(request.getAttribute(
    @@ -1401,7 +1370,7 @@ public void testWithFilterConfig() throws ServletException {
             Assert.assertTrue(corsFilter.getAllowedOrigins().size() == 0);
             Assert.assertTrue(corsFilter.isAnyOriginAllowed());
             Assert.assertTrue(corsFilter.getExposedHeaders().size() == 0);
    -        Assert.assertTrue(corsFilter.isSupportsCredentials());
    +        Assert.assertFalse(corsFilter.isSupportsCredentials());
             Assert.assertTrue(corsFilter.getPreflightMaxAge() == 1800);
         }
     
    @@ -1437,9 +1406,9 @@ public void testWithStringParserNull() throws ServletException {
             Assert.assertTrue(corsFilter.getAllowedHttpHeaders().size() == 6);
             Assert.assertTrue(corsFilter.getAllowedHttpMethods().size() == 4);
             Assert.assertTrue(corsFilter.getAllowedOrigins().size() == 0);
    -        Assert.assertTrue(corsFilter.isAnyOriginAllowed());
    +        Assert.assertFalse(corsFilter.isAnyOriginAllowed());
             Assert.assertTrue(corsFilter.getExposedHeaders().size() == 0);
    -        Assert.assertTrue(corsFilter.isSupportsCredentials());
    +        Assert.assertFalse(corsFilter.isSupportsCredentials());
             Assert.assertTrue(corsFilter.getPreflightMaxAge() == 1800);
         }
     
    @@ -1543,8 +1512,7 @@ public void testDecorateRequestDisabled() throws IOException,
             corsFilter.doFilter(request, response, filterChain);
     
             Assert.assertTrue(response.getHeader(
    -                CorsFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN).equals(
    -                "https://www.apache.org"));
    +                CorsFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN).equals("*"));
             Assert.assertNull(request
                     .getAttribute(CorsFilter.HTTP_REQUEST_ATTRIBUTE_IS_CORS_REQUEST));
             Assert.assertNull(request
    
  • test/org/apache/catalina/filters/TesterFilterConfigs.java+6 5 modified
    @@ -36,12 +36,13 @@ public class TesterFilterConfigs {
         public static final TesterServletContext mockServletContext =
                 new TesterServletContext();
     
    +    // Default config for the test is to allow any origin
         public static FilterConfig getDefaultFilterConfig() {
             final String allowedHttpHeaders =
                     CorsFilter.DEFAULT_ALLOWED_HTTP_HEADERS;
             final String allowedHttpMethods =
                     CorsFilter.DEFAULT_ALLOWED_HTTP_METHODS;
    -        final String allowedOrigins = CorsFilter.DEFAULT_ALLOWED_ORIGINS;
    +        final String allowedOrigins = ANY_ORIGIN;
             final String exposedHeaders = CorsFilter.DEFAULT_EXPOSED_HEADERS;
             final String supportCredentials =
                     CorsFilter.DEFAULT_SUPPORTS_CREDENTIALS;
    @@ -59,7 +60,7 @@ public static FilterConfig getFilterConfigAnyOriginAndSupportsCredentials() {
                     CorsFilter.DEFAULT_ALLOWED_HTTP_HEADERS;
             final String allowedHttpMethods =
                     CorsFilter.DEFAULT_ALLOWED_HTTP_METHODS + ",PUT";
    -        final String allowedOrigins = CorsFilter.DEFAULT_ALLOWED_ORIGINS;
    +        final String allowedOrigins = ANY_ORIGIN;
             final String exposedHeaders = CorsFilter.DEFAULT_EXPOSED_HEADERS;
             final String supportCredentials = "true";
             final String preflightMaxAge =
    @@ -77,7 +78,7 @@ public static FilterConfig getFilterConfigAnyOriginAndSupportsCredentials() {
                     CorsFilter.DEFAULT_ALLOWED_HTTP_HEADERS;
             final String allowedHttpMethods =
                     CorsFilter.DEFAULT_ALLOWED_HTTP_METHODS + ",PUT";
    -        final String allowedOrigins = CorsFilter.DEFAULT_ALLOWED_ORIGINS;
    +        final String allowedOrigins = ANY_ORIGIN;
             final String exposedHeaders = CorsFilter.DEFAULT_EXPOSED_HEADERS;
             final String supportCredentials = "false";
             final String preflightMaxAge =
    @@ -131,7 +132,7 @@ public static FilterConfig getFilterConfigWithExposedHeaders() {
                     CorsFilter.DEFAULT_ALLOWED_HTTP_HEADERS;
             final String allowedHttpMethods =
                     CorsFilter.DEFAULT_ALLOWED_HTTP_METHODS;
    -        final String allowedOrigins = CorsFilter.DEFAULT_ALLOWED_ORIGINS;
    +        final String allowedOrigins = ANY_ORIGIN;
             final String exposedHeaders = EXPOSED_HEADERS;
             final String supportCredentials =
                     CorsFilter.DEFAULT_SUPPORTS_CREDENTIALS;
    @@ -240,7 +241,7 @@ public static FilterConfig getFilterConfigDecorateRequestDisabled() {
                     CorsFilter.DEFAULT_ALLOWED_HTTP_HEADERS;
             final String allowedHttpMethods =
                     CorsFilter.DEFAULT_ALLOWED_HTTP_METHODS;
    -        final String allowedOrigins = CorsFilter.DEFAULT_ALLOWED_ORIGINS;
    +        final String allowedOrigins = ANY_ORIGIN;
             final String exposedHeaders = CorsFilter.DEFAULT_EXPOSED_HEADERS;
             final String supportCredentials =
                     CorsFilter.DEFAULT_SUPPORTS_CREDENTIALS;
    
  • webapps/docs/changelog.xml+4 0 modified
    @@ -70,6 +70,10 @@
             <code>@Resource</code> annotations specify a name with an explicit
             <code>java:</code> namespace. (markt)
           </fix>
    +      <fix>
    +        <bug>62343</bug>: Make CORS filter defaults more secure. This is the fix
    +        for CVE-2018-8014. (markt)
    +      </fix>
         </changelog>
       </subsection>
       <subsection name="Coyote">
    
2c9d8433bd32

Fix https://bz.apache.org/bugzilla/show_bug.cgi?id=62343

https://github.com/apache/tomcat80Mark ThomasMay 16, 2018via ghsa
5 files changed · +37 61
  • java/org/apache/catalina/filters/CorsFilter.java+10 9 modified
    @@ -278,17 +278,14 @@ protected void handleSimpleCORS(final HttpServletRequest request,
     
             // Section 6.1.3
             // Add a single Access-Control-Allow-Origin header.
    -        if (anyOriginAllowed && !supportsCredentials) {
    -            // If resource doesn't support credentials and if any origin is
    -            // allowed
    -            // to make CORS request, return header with '*'.
    +        if (anyOriginAllowed) {
    +            // If any origin is allowed, return header with '*'.
                 response.addHeader(
                         CorsFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN,
                         "*");
             } else {
    -            // If the resource supports credentials add a single
    -            // Access-Control-Allow-Origin header, with the value of the Origin
    -            // header as value.
    +            // Add a single Access-Control-Allow-Origin header, with the value
    +            // of the Origin header as value.
                 response.addHeader(
                         CorsFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN,
                         origin);
    @@ -804,6 +801,10 @@ private void parseAndStore(final String allowedOrigins,
             // For any value other then 'true' this will be false.
             this.supportsCredentials = Boolean.parseBoolean(supportsCredentials);
     
    +        if (this.supportsCredentials && this.anyOriginAllowed) {
    +            throw new ServletException(sm.getString("corsFilter.invalidSupportsCredentials"));
    +        }
    +
             try {
                 if (!preflightMaxAge.isEmpty()) {
                     this.preflightMaxAge = Long.parseLong(preflightMaxAge);
    @@ -1163,7 +1164,7 @@ protected enum CORSRequestType {
         /**
          * By default, all origins are allowed to make requests.
          */
    -    public static final String DEFAULT_ALLOWED_ORIGINS = "*";
    +    public static final String DEFAULT_ALLOWED_ORIGINS = "";
     
         /**
          * By default, following methods are supported: GET, POST, HEAD and OPTIONS.
    @@ -1179,7 +1180,7 @@ protected enum CORSRequestType {
         /**
          * By default, support credentials is turned on.
          */
    -    public static final String DEFAULT_SUPPORTS_CREDENTIALS = "true";
    +    public static final String DEFAULT_SUPPORTS_CREDENTIALS = "false";
     
         /**
          * By default, following headers are supported:
    
  • java/org/apache/catalina/filters/LocalStrings.properties+2 0 modified
    @@ -14,6 +14,8 @@
     # limitations under the License.
     
     addDefaultCharset.unsupportedCharset=Specified character set [{0}] is not supported
    +
    +corsFilter.invalidSupportsCredentials=It is not allowed to configure supportsCredentials=[true] when allowedOrigins=[*]
     corsFilter.invalidPreflightMaxAge=Unable to parse preflightMaxAge
     corsFilter.nullRequest=HttpServletRequest object is null
     corsFilter.nullRequestType=CORSRequestType object is null
    
  • test/org/apache/catalina/filters/TestCorsFilter.java+15 47 modified
    @@ -52,8 +52,7 @@ public void testDoFilterSimpleGET() throws IOException, ServletException {
             corsFilter.doFilter(request, response, filterChain);
     
             Assert.assertTrue(response.getHeader(
    -                CorsFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN).equals(
    -                "https://www.apache.org"));
    +                CorsFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN).equals("*"));
             Assert.assertTrue(((Boolean) request.getAttribute(
                     CorsFilter.HTTP_REQUEST_ATTRIBUTE_IS_CORS_REQUEST)).booleanValue());
             Assert.assertTrue(request.getAttribute(
    @@ -85,8 +84,7 @@ public void testDoFilterSimplePOST() throws IOException, ServletException {
             corsFilter.doFilter(request, response, filterChain);
     
             Assert.assertTrue(response.getHeader(
    -                CorsFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN).equals(
    -                "https://www.apache.org"));
    +                CorsFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN).equals("*"));
             Assert.assertTrue(((Boolean) request.getAttribute(
                     CorsFilter.HTTP_REQUEST_ATTRIBUTE_IS_CORS_REQUEST)).booleanValue());
             Assert.assertTrue(request.getAttribute(
    @@ -117,8 +115,7 @@ public void testDoFilterSimpleHEAD() throws IOException, ServletException {
             corsFilter.doFilter(request, response, filterChain);
     
             Assert.assertTrue(response.getHeader(
    -                CorsFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN).equals(
    -                "https://www.apache.org"));
    +                CorsFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN).equals("*"));
             Assert.assertTrue(((Boolean) request.getAttribute(
                     CorsFilter.HTTP_REQUEST_ATTRIBUTE_IS_CORS_REQUEST)).booleanValue());
             Assert.assertTrue(request.getAttribute(
    @@ -163,41 +160,15 @@ public void testDoFilterSimpleSpecificHeader() throws IOException,
         }
     
         /*
    -     * Tests the presence of the origin (and not '*') in the response, when
    -     * supports credentials is enabled alongwith any origin, '*'.
    +     * Tests the that supports credentials may not be enabled with any origin,
    +     * '*'.
          *
    -     * @throws IOException
          * @throws ServletException
          */
    -    @Test
    -    public void testDoFilterSimpleAnyOriginAndSupportsCredentials()
    -            throws IOException, ServletException {
    -        TesterHttpServletRequest request = new TesterHttpServletRequest();
    -        request.setHeader(CorsFilter.REQUEST_HEADER_ORIGIN,
    -                TesterFilterConfigs.HTTPS_WWW_APACHE_ORG);
    -        request.setMethod("GET");
    -        TesterHttpServletResponse response = new TesterHttpServletResponse();
    -
    +    @Test(expected=ServletException.class)
    +    public void testDoFilterSimpleAnyOriginAndSupportsCredentials() throws ServletException {
             CorsFilter corsFilter = new CorsFilter();
    -        corsFilter.init(TesterFilterConfigs
    -                .getFilterConfigAnyOriginAndSupportsCredentials());
    -        corsFilter.doFilter(request, response, filterChain);
    -
    -        Assert.assertTrue(response.getHeader(
    -                CorsFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN).equals(
    -                TesterFilterConfigs.HTTPS_WWW_APACHE_ORG));
    -        Assert.assertTrue(response.getHeader(
    -                CorsFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_CREDENTIALS)
    -                .equals(
    -                        "true"));
    -        Assert.assertTrue(((Boolean) request.getAttribute(
    -                CorsFilter.HTTP_REQUEST_ATTRIBUTE_IS_CORS_REQUEST)).booleanValue());
    -        Assert.assertTrue(request.getAttribute(
    -                CorsFilter.HTTP_REQUEST_ATTRIBUTE_ORIGIN).equals(
    -                TesterFilterConfigs.HTTPS_WWW_APACHE_ORG));
    -        Assert.assertTrue(request.getAttribute(
    -                CorsFilter.HTTP_REQUEST_ATTRIBUTE_REQUEST_TYPE).equals(
    -                CorsFilter.CORSRequestType.SIMPLE.name().toLowerCase(Locale.ENGLISH)));
    +        corsFilter.init(TesterFilterConfigs.getFilterConfigAnyOriginAndSupportsCredentials());
         }
     
         /*
    @@ -258,8 +229,7 @@ public void testDoFilterSimpleWithExposedHeaders() throws IOException,
             corsFilter.doFilter(request, response, filterChain);
     
             Assert.assertTrue(response.getHeader(
    -                CorsFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN).equals(
    -                "https://www.apache.org"));
    +                CorsFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN).equals("*"));
             Assert.assertTrue(response.getHeader(
                     CorsFilter.RESPONSE_HEADER_ACCESS_CONTROL_EXPOSE_HEADERS)
                     .equals(TesterFilterConfigs.EXPOSED_HEADERS));
    @@ -707,9 +677,8 @@ public void testInitDefaultFilterConfig() throws IOException,
             corsFilter.init(null);
             corsFilter.doFilter(request, response, filterChain);
     
    -        Assert.assertTrue(response.getHeader(
    -                CorsFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN).equals(
    -                "https://www.apache.org"));
    +        Assert.assertNull(response.getHeader(
    +                CorsFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN));
             Assert.assertTrue(((Boolean) request.getAttribute(
                     CorsFilter.HTTP_REQUEST_ATTRIBUTE_IS_CORS_REQUEST)).booleanValue());
             Assert.assertTrue(request.getAttribute(
    @@ -1392,7 +1361,7 @@ public void testWithFilterConfig() throws ServletException {
             Assert.assertTrue(corsFilter.getAllowedOrigins().size() == 0);
             Assert.assertTrue(corsFilter.isAnyOriginAllowed());
             Assert.assertTrue(corsFilter.getExposedHeaders().size() == 0);
    -        Assert.assertTrue(corsFilter.isSupportsCredentials());
    +        Assert.assertFalse(corsFilter.isSupportsCredentials());
             Assert.assertTrue(corsFilter.getPreflightMaxAge() == 1800);
         }
     
    @@ -1428,9 +1397,9 @@ public void testWithStringParserNull() throws ServletException {
             Assert.assertTrue(corsFilter.getAllowedHttpHeaders().size() == 6);
             Assert.assertTrue(corsFilter.getAllowedHttpMethods().size() == 4);
             Assert.assertTrue(corsFilter.getAllowedOrigins().size() == 0);
    -        Assert.assertTrue(corsFilter.isAnyOriginAllowed());
    +        Assert.assertFalse(corsFilter.isAnyOriginAllowed());
             Assert.assertTrue(corsFilter.getExposedHeaders().size() == 0);
    -        Assert.assertTrue(corsFilter.isSupportsCredentials());
    +        Assert.assertFalse(corsFilter.isSupportsCredentials());
             Assert.assertTrue(corsFilter.getPreflightMaxAge() == 1800);
         }
     
    @@ -1534,8 +1503,7 @@ public void testDecorateRequestDisabled() throws IOException,
             corsFilter.doFilter(request, response, filterChain);
     
             Assert.assertTrue(response.getHeader(
    -                CorsFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN).equals(
    -                "https://www.apache.org"));
    +                CorsFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN).equals("*"));
             Assert.assertNull(request
                     .getAttribute(CorsFilter.HTTP_REQUEST_ATTRIBUTE_IS_CORS_REQUEST));
             Assert.assertNull(request
    
  • test/org/apache/catalina/filters/TesterFilterConfigs.java+6 5 modified
    @@ -36,12 +36,13 @@ public class TesterFilterConfigs {
         public static final TesterServletContext mockServletContext =
                 new TesterServletContext();
     
    +    // Default config for the test is to allow any origin
         public static FilterConfig getDefaultFilterConfig() {
             final String allowedHttpHeaders =
                     CorsFilter.DEFAULT_ALLOWED_HTTP_HEADERS;
             final String allowedHttpMethods =
                     CorsFilter.DEFAULT_ALLOWED_HTTP_METHODS;
    -        final String allowedOrigins = CorsFilter.DEFAULT_ALLOWED_ORIGINS;
    +        final String allowedOrigins = ANY_ORIGIN;
             final String exposedHeaders = CorsFilter.DEFAULT_EXPOSED_HEADERS;
             final String supportCredentials =
                     CorsFilter.DEFAULT_SUPPORTS_CREDENTIALS;
    @@ -59,7 +60,7 @@ public static FilterConfig getFilterConfigAnyOriginAndSupportsCredentials() {
                     CorsFilter.DEFAULT_ALLOWED_HTTP_HEADERS;
             final String allowedHttpMethods =
                     CorsFilter.DEFAULT_ALLOWED_HTTP_METHODS + ",PUT";
    -        final String allowedOrigins = CorsFilter.DEFAULT_ALLOWED_ORIGINS;
    +        final String allowedOrigins = ANY_ORIGIN;
             final String exposedHeaders = CorsFilter.DEFAULT_EXPOSED_HEADERS;
             final String supportCredentials = "true";
             final String preflightMaxAge =
    @@ -77,7 +78,7 @@ public static FilterConfig getFilterConfigAnyOriginAndSupportsCredentials() {
                     CorsFilter.DEFAULT_ALLOWED_HTTP_HEADERS;
             final String allowedHttpMethods =
                     CorsFilter.DEFAULT_ALLOWED_HTTP_METHODS + ",PUT";
    -        final String allowedOrigins = CorsFilter.DEFAULT_ALLOWED_ORIGINS;
    +        final String allowedOrigins = ANY_ORIGIN;
             final String exposedHeaders = CorsFilter.DEFAULT_EXPOSED_HEADERS;
             final String supportCredentials = "false";
             final String preflightMaxAge =
    @@ -131,7 +132,7 @@ public static FilterConfig getFilterConfigWithExposedHeaders() {
                     CorsFilter.DEFAULT_ALLOWED_HTTP_HEADERS;
             final String allowedHttpMethods =
                     CorsFilter.DEFAULT_ALLOWED_HTTP_METHODS;
    -        final String allowedOrigins = CorsFilter.DEFAULT_ALLOWED_ORIGINS;
    +        final String allowedOrigins = ANY_ORIGIN;
             final String exposedHeaders = EXPOSED_HEADERS;
             final String supportCredentials =
                     CorsFilter.DEFAULT_SUPPORTS_CREDENTIALS;
    @@ -240,7 +241,7 @@ public static FilterConfig getFilterConfigDecorateRequestDisabled() {
                     CorsFilter.DEFAULT_ALLOWED_HTTP_HEADERS;
             final String allowedHttpMethods =
                     CorsFilter.DEFAULT_ALLOWED_HTTP_METHODS;
    -        final String allowedOrigins = CorsFilter.DEFAULT_ALLOWED_ORIGINS;
    +        final String allowedOrigins = ANY_ORIGIN;
             final String exposedHeaders = CorsFilter.DEFAULT_EXPOSED_HEADERS;
             final String supportCredentials =
                     CorsFilter.DEFAULT_SUPPORTS_CREDENTIALS;
    
  • webapps/docs/changelog.xml+4 0 modified
    @@ -89,6 +89,10 @@
             <bug>60490</bug>: Various formatting and layout improvements for the
             <code>ErrorReportValve</code>. Patch provided by Michael Osipov. (markt)
           </fix>
    +      <fix>
    +        <bug>62343</bug>: Make CORS filter defaults more secure. This is the fix
    +        for CVE-2018-8014. (markt)
    +      </fix>
         </changelog>
       </subsection>
       <subsection name="Coyote">
    
60f596a21fd6

Fix https://bz.apache.org/bugzilla/show_bug.cgi?id=62343

https://github.com/apache/tomcatMark ThomasMay 16, 2018via ghsa
5 files changed · +37 61
  • java/org/apache/catalina/filters/CorsFilter.java+10 9 modified
    @@ -267,17 +267,14 @@ protected void handleSimpleCORS(final HttpServletRequest request,
     
             // Section 6.1.3
             // Add a single Access-Control-Allow-Origin header.
    -        if (anyOriginAllowed && !supportsCredentials) {
    -            // If resource doesn't support credentials and if any origin is
    -            // allowed
    -            // to make CORS request, return header with '*'.
    +        if (anyOriginAllowed) {
    +            // If any origin is allowed, return header with '*'.
                 response.addHeader(
                         CorsFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN,
                         "*");
             } else {
    -            // If the resource supports credentials add a single
    -            // Access-Control-Allow-Origin header, with the value of the Origin
    -            // header as value.
    +            // Add a single Access-Control-Allow-Origin header, with the value
    +            // of the Origin header as value.
                 response.addHeader(
                         CorsFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN,
                         origin);
    @@ -781,6 +778,10 @@ private void parseAndStore(final String allowedOrigins,
             // For any value other then 'true' this will be false.
             this.supportsCredentials = Boolean.parseBoolean(supportsCredentials);
     
    +        if (this.supportsCredentials && this.anyOriginAllowed) {
    +            throw new ServletException(sm.getString("corsFilter.invalidSupportsCredentials"));
    +        }
    +
             try {
                 if (!preflightMaxAge.isEmpty()) {
                     this.preflightMaxAge = Long.parseLong(preflightMaxAge);
    @@ -1090,7 +1091,7 @@ protected enum CORSRequestType {
         /**
          * By default, all origins are allowed to make requests.
          */
    -    public static final String DEFAULT_ALLOWED_ORIGINS = "*";
    +    public static final String DEFAULT_ALLOWED_ORIGINS = "";
     
         /**
          * By default, following methods are supported: GET, POST, HEAD and OPTIONS.
    @@ -1106,7 +1107,7 @@ protected enum CORSRequestType {
         /**
          * By default, support credentials is turned on.
          */
    -    public static final String DEFAULT_SUPPORTS_CREDENTIALS = "true";
    +    public static final String DEFAULT_SUPPORTS_CREDENTIALS = "false";
     
         /**
          * By default, following headers are supported:
    
  • java/org/apache/catalina/filters/LocalStrings.properties+2 0 modified
    @@ -14,6 +14,8 @@
     # limitations under the License.
     
     addDefaultCharset.unsupportedCharset=Specified character set [{0}] is not supported
    +
    +corsFilter.invalidSupportsCredentials=It is not allowed to configure supportsCredentials=[true] when allowedOrigins=[*]
     corsFilter.invalidPreflightMaxAge=Unable to parse preflightMaxAge
     corsFilter.nullRequest=HttpServletRequest object is null
     corsFilter.nullRequestType=CORSRequestType object is null
    
  • test/org/apache/catalina/filters/TestCorsFilter.java+15 47 modified
    @@ -55,8 +55,7 @@ public void testDoFilterSimpleGET() throws IOException, ServletException {
             corsFilter.doFilter(request, response, filterChain);
     
             Assert.assertTrue(response.getHeader(
    -                CorsFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN).equals(
    -                "https://www.apache.org"));
    +                CorsFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN).equals("*"));
             Assert.assertTrue(((Boolean) request.getAttribute(
                     CorsFilter.HTTP_REQUEST_ATTRIBUTE_IS_CORS_REQUEST)).booleanValue());
             Assert.assertTrue(request.getAttribute(
    @@ -88,8 +87,7 @@ public void testDoFilterSimplePOST() throws IOException, ServletException {
             corsFilter.doFilter(request, response, filterChain);
     
             Assert.assertTrue(response.getHeader(
    -                CorsFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN).equals(
    -                "https://www.apache.org"));
    +                CorsFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN).equals("*"));
             Assert.assertTrue(((Boolean) request.getAttribute(
                     CorsFilter.HTTP_REQUEST_ATTRIBUTE_IS_CORS_REQUEST)).booleanValue());
             Assert.assertTrue(request.getAttribute(
    @@ -120,8 +118,7 @@ public void testDoFilterSimpleHEAD() throws IOException, ServletException {
             corsFilter.doFilter(request, response, filterChain);
     
             Assert.assertTrue(response.getHeader(
    -                CorsFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN).equals(
    -                "https://www.apache.org"));
    +                CorsFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN).equals("*"));
             Assert.assertTrue(((Boolean) request.getAttribute(
                     CorsFilter.HTTP_REQUEST_ATTRIBUTE_IS_CORS_REQUEST)).booleanValue());
             Assert.assertTrue(request.getAttribute(
    @@ -166,41 +163,15 @@ public void testDoFilterSimpleSpecificHeader() throws IOException,
         }
     
         /*
    -     * Tests the presence of the origin (and not '*') in the response, when
    -     * supports credentials is enabled alongwith any origin, '*'.
    +     * Tests the that supports credentials may not be enabled with any origin,
    +     * '*'.
          *
    -     * @throws IOException
          * @throws ServletException
          */
    -    @Test
    -    public void testDoFilterSimpleAnyOriginAndSupportsCredentials()
    -            throws IOException, ServletException {
    -        TesterHttpServletRequest request = new TesterHttpServletRequest();
    -        request.setHeader(CorsFilter.REQUEST_HEADER_ORIGIN,
    -                TesterFilterConfigs.HTTPS_WWW_APACHE_ORG);
    -        request.setMethod("GET");
    -        TesterHttpServletResponse response = new TesterHttpServletResponse();
    -
    +    @Test(expected=ServletException.class)
    +    public void testDoFilterSimpleAnyOriginAndSupportsCredentials() throws ServletException {
             CorsFilter corsFilter = new CorsFilter();
    -        corsFilter.init(TesterFilterConfigs
    -                .getFilterConfigAnyOriginAndSupportsCredentials());
    -        corsFilter.doFilter(request, response, filterChain);
    -
    -        Assert.assertTrue(response.getHeader(
    -                CorsFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN).equals(
    -                TesterFilterConfigs.HTTPS_WWW_APACHE_ORG));
    -        Assert.assertTrue(response.getHeader(
    -                CorsFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_CREDENTIALS)
    -                .equals(
    -                        "true"));
    -        Assert.assertTrue(((Boolean) request.getAttribute(
    -                CorsFilter.HTTP_REQUEST_ATTRIBUTE_IS_CORS_REQUEST)).booleanValue());
    -        Assert.assertTrue(request.getAttribute(
    -                CorsFilter.HTTP_REQUEST_ATTRIBUTE_ORIGIN).equals(
    -                TesterFilterConfigs.HTTPS_WWW_APACHE_ORG));
    -        Assert.assertTrue(request.getAttribute(
    -                CorsFilter.HTTP_REQUEST_ATTRIBUTE_REQUEST_TYPE).equals(
    -                CorsFilter.CORSRequestType.SIMPLE.name().toLowerCase(Locale.ENGLISH)));
    +        corsFilter.init(TesterFilterConfigs.getFilterConfigAnyOriginAndSupportsCredentials());
         }
     
         /*
    @@ -261,8 +232,7 @@ public void testDoFilterSimpleWithExposedHeaders() throws IOException,
             corsFilter.doFilter(request, response, filterChain);
     
             Assert.assertTrue(response.getHeader(
    -                CorsFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN).equals(
    -                "https://www.apache.org"));
    +                CorsFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN).equals("*"));
             Assert.assertTrue(response.getHeader(
                     CorsFilter.RESPONSE_HEADER_ACCESS_CONTROL_EXPOSE_HEADERS)
                     .equals(TesterFilterConfigs.EXPOSED_HEADERS));
    @@ -727,9 +697,8 @@ public String getFilterName() {
             });
             corsFilter.doFilter(request, response, filterChain);
     
    -        Assert.assertTrue(response.getHeader(
    -                CorsFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN).equals(
    -                "https://www.apache.org"));
    +        Assert.assertNull(response.getHeader(
    +                CorsFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN));
             Assert.assertTrue(((Boolean) request.getAttribute(
                     CorsFilter.HTTP_REQUEST_ATTRIBUTE_IS_CORS_REQUEST)).booleanValue());
             Assert.assertTrue(request.getAttribute(
    @@ -1412,7 +1381,7 @@ public void testWithFilterConfig() throws ServletException {
             Assert.assertTrue(corsFilter.getAllowedOrigins().size() == 0);
             Assert.assertTrue(corsFilter.isAnyOriginAllowed());
             Assert.assertTrue(corsFilter.getExposedHeaders().size() == 0);
    -        Assert.assertTrue(corsFilter.isSupportsCredentials());
    +        Assert.assertFalse(corsFilter.isSupportsCredentials());
             Assert.assertTrue(corsFilter.getPreflightMaxAge() == 1800);
         }
     
    @@ -1448,9 +1417,9 @@ public void testWithStringParserNull() throws ServletException {
             Assert.assertTrue(corsFilter.getAllowedHttpHeaders().size() == 6);
             Assert.assertTrue(corsFilter.getAllowedHttpMethods().size() == 4);
             Assert.assertTrue(corsFilter.getAllowedOrigins().size() == 0);
    -        Assert.assertTrue(corsFilter.isAnyOriginAllowed());
    +        Assert.assertFalse(corsFilter.isAnyOriginAllowed());
             Assert.assertTrue(corsFilter.getExposedHeaders().size() == 0);
    -        Assert.assertTrue(corsFilter.isSupportsCredentials());
    +        Assert.assertFalse(corsFilter.isSupportsCredentials());
             Assert.assertTrue(corsFilter.getPreflightMaxAge() == 1800);
         }
     
    @@ -1554,8 +1523,7 @@ public void testDecorateRequestDisabled() throws IOException,
             corsFilter.doFilter(request, response, filterChain);
     
             Assert.assertTrue(response.getHeader(
    -                CorsFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN).equals(
    -                "https://www.apache.org"));
    +                CorsFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN).equals("*"));
             Assert.assertNull(request
                     .getAttribute(CorsFilter.HTTP_REQUEST_ATTRIBUTE_IS_CORS_REQUEST));
             Assert.assertNull(request
    
  • test/org/apache/catalina/filters/TesterFilterConfigs.java+6 5 modified
    @@ -36,12 +36,13 @@ public class TesterFilterConfigs {
         public static final TesterServletContext mockServletContext =
                 new TesterServletContext();
     
    +    // Default config for the test is to allow any origin
         public static FilterConfig getDefaultFilterConfig() {
             final String allowedHttpHeaders =
                     CorsFilter.DEFAULT_ALLOWED_HTTP_HEADERS;
             final String allowedHttpMethods =
                     CorsFilter.DEFAULT_ALLOWED_HTTP_METHODS;
    -        final String allowedOrigins = CorsFilter.DEFAULT_ALLOWED_ORIGINS;
    +        final String allowedOrigins = ANY_ORIGIN;
             final String exposedHeaders = CorsFilter.DEFAULT_EXPOSED_HEADERS;
             final String supportCredentials =
                     CorsFilter.DEFAULT_SUPPORTS_CREDENTIALS;
    @@ -59,7 +60,7 @@ public static FilterConfig getFilterConfigAnyOriginAndSupportsCredentials() {
                     CorsFilter.DEFAULT_ALLOWED_HTTP_HEADERS;
             final String allowedHttpMethods =
                     CorsFilter.DEFAULT_ALLOWED_HTTP_METHODS + ",PUT";
    -        final String allowedOrigins = CorsFilter.DEFAULT_ALLOWED_ORIGINS;
    +        final String allowedOrigins = ANY_ORIGIN;
             final String exposedHeaders = CorsFilter.DEFAULT_EXPOSED_HEADERS;
             final String supportCredentials = "true";
             final String preflightMaxAge =
    @@ -77,7 +78,7 @@ public static FilterConfig getFilterConfigAnyOriginAndSupportsCredentials() {
                     CorsFilter.DEFAULT_ALLOWED_HTTP_HEADERS;
             final String allowedHttpMethods =
                     CorsFilter.DEFAULT_ALLOWED_HTTP_METHODS + ",PUT";
    -        final String allowedOrigins = CorsFilter.DEFAULT_ALLOWED_ORIGINS;
    +        final String allowedOrigins = ANY_ORIGIN;
             final String exposedHeaders = CorsFilter.DEFAULT_EXPOSED_HEADERS;
             final String supportCredentials = "false";
             final String preflightMaxAge =
    @@ -131,7 +132,7 @@ public static FilterConfig getFilterConfigWithExposedHeaders() {
                     CorsFilter.DEFAULT_ALLOWED_HTTP_HEADERS;
             final String allowedHttpMethods =
                     CorsFilter.DEFAULT_ALLOWED_HTTP_METHODS;
    -        final String allowedOrigins = CorsFilter.DEFAULT_ALLOWED_ORIGINS;
    +        final String allowedOrigins = ANY_ORIGIN;
             final String exposedHeaders = EXPOSED_HEADERS;
             final String supportCredentials =
                     CorsFilter.DEFAULT_SUPPORTS_CREDENTIALS;
    @@ -240,7 +241,7 @@ public static FilterConfig getFilterConfigDecorateRequestDisabled() {
                     CorsFilter.DEFAULT_ALLOWED_HTTP_HEADERS;
             final String allowedHttpMethods =
                     CorsFilter.DEFAULT_ALLOWED_HTTP_METHODS;
    -        final String allowedOrigins = CorsFilter.DEFAULT_ALLOWED_ORIGINS;
    +        final String allowedOrigins = ANY_ORIGIN;
             final String exposedHeaders = CorsFilter.DEFAULT_EXPOSED_HEADERS;
             final String supportCredentials =
                     CorsFilter.DEFAULT_SUPPORTS_CREDENTIALS;
    
  • webapps/docs/changelog.xml+4 0 modified
    @@ -85,6 +85,10 @@
             <bug>50019</bug>: Add support for <code>&lt;lookup-name&gt;</code>.
             Based on a patch by Gurkan Erdogdu. (markt)
           </fix>
    +      <fix>
    +        <bug>62343</bug>: Make CORS filter defaults more secure. This is the fix
    +        for CVE-2018-8014. (markt)
    +      </fix>
         </changelog>
       </subsection>
       <subsection name="Coyote">
    
d83a76732e68

Fix https://bz.apache.org/bugzilla/show_bug.cgi?id=62343

https://github.com/apache/tomcatMark ThomasMay 16, 2018via ghsa
5 files changed · +37 61
  • java/org/apache/catalina/filters/CorsFilter.java+10 9 modified
    @@ -256,17 +256,14 @@ protected void handleSimpleCORS(final HttpServletRequest request,
     
             // Section 6.1.3
             // Add a single Access-Control-Allow-Origin header.
    -        if (anyOriginAllowed && !supportsCredentials) {
    -            // If resource doesn't support credentials and if any origin is
    -            // allowed
    -            // to make CORS request, return header with '*'.
    +        if (anyOriginAllowed) {
    +            // If any origin is allowed, return header with '*'.
                 response.addHeader(
                         CorsFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN,
                         "*");
             } else {
    -            // If the resource supports credentials add a single
    -            // Access-Control-Allow-Origin header, with the value of the Origin
    -            // header as value.
    +            // Add a single Access-Control-Allow-Origin header, with the value
    +            // of the Origin header as value.
                 response.addHeader(
                         CorsFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN,
                         origin);
    @@ -764,6 +761,10 @@ private void parseAndStore(final String allowedOrigins,
             // For any value other then 'true' this will be false.
             this.supportsCredentials = Boolean.parseBoolean(supportsCredentials);
     
    +        if (this.supportsCredentials && this.anyOriginAllowed) {
    +            throw new ServletException(sm.getString("corsFilter.invalidSupportsCredentials"));
    +        }
    +
             try {
                 if (!preflightMaxAge.isEmpty()) {
                     this.preflightMaxAge = Long.parseLong(preflightMaxAge);
    @@ -1073,7 +1074,7 @@ protected enum CORSRequestType {
         /**
          * By default, all origins are allowed to make requests.
          */
    -    public static final String DEFAULT_ALLOWED_ORIGINS = "*";
    +    public static final String DEFAULT_ALLOWED_ORIGINS = "";
     
         /**
          * By default, following methods are supported: GET, POST, HEAD and OPTIONS.
    @@ -1089,7 +1090,7 @@ protected enum CORSRequestType {
         /**
          * By default, support credentials is turned on.
          */
    -    public static final String DEFAULT_SUPPORTS_CREDENTIALS = "true";
    +    public static final String DEFAULT_SUPPORTS_CREDENTIALS = "false";
     
         /**
          * By default, following headers are supported:
    
  • java/org/apache/catalina/filters/LocalStrings.properties+2 0 modified
    @@ -14,6 +14,8 @@
     # limitations under the License.
     
     addDefaultCharset.unsupportedCharset=Specified character set [{0}] is not supported
    +
    +corsFilter.invalidSupportsCredentials=It is not allowed to configure supportsCredentials=[true] when allowedOrigins=[*]
     corsFilter.invalidPreflightMaxAge=Unable to parse preflightMaxAge
     corsFilter.nullRequest=HttpServletRequest object is null
     corsFilter.nullRequestType=CORSRequestType object is null
    
  • test/org/apache/catalina/filters/TestCorsFilter.java+15 47 modified
    @@ -55,8 +55,7 @@ public void testDoFilterSimpleGET() throws IOException, ServletException {
             corsFilter.doFilter(request, response, filterChain);
     
             Assert.assertTrue(response.getHeader(
    -                CorsFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN).equals(
    -                "https://www.apache.org"));
    +                CorsFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN).equals("*"));
             Assert.assertTrue(((Boolean) request.getAttribute(
                     CorsFilter.HTTP_REQUEST_ATTRIBUTE_IS_CORS_REQUEST)).booleanValue());
             Assert.assertTrue(request.getAttribute(
    @@ -88,8 +87,7 @@ public void testDoFilterSimplePOST() throws IOException, ServletException {
             corsFilter.doFilter(request, response, filterChain);
     
             Assert.assertTrue(response.getHeader(
    -                CorsFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN).equals(
    -                "https://www.apache.org"));
    +                CorsFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN).equals("*"));
             Assert.assertTrue(((Boolean) request.getAttribute(
                     CorsFilter.HTTP_REQUEST_ATTRIBUTE_IS_CORS_REQUEST)).booleanValue());
             Assert.assertTrue(request.getAttribute(
    @@ -120,8 +118,7 @@ public void testDoFilterSimpleHEAD() throws IOException, ServletException {
             corsFilter.doFilter(request, response, filterChain);
     
             Assert.assertTrue(response.getHeader(
    -                CorsFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN).equals(
    -                "https://www.apache.org"));
    +                CorsFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN).equals("*"));
             Assert.assertTrue(((Boolean) request.getAttribute(
                     CorsFilter.HTTP_REQUEST_ATTRIBUTE_IS_CORS_REQUEST)).booleanValue());
             Assert.assertTrue(request.getAttribute(
    @@ -166,41 +163,15 @@ public void testDoFilterSimpleSpecificHeader() throws IOException,
         }
     
         /*
    -     * Tests the presence of the origin (and not '*') in the response, when
    -     * supports credentials is enabled alongwith any origin, '*'.
    +     * Tests the that supports credentials may not be enabled with any origin,
    +     * '*'.
          *
    -     * @throws IOException
          * @throws ServletException
          */
    -    @Test
    -    public void testDoFilterSimpleAnyOriginAndSupportsCredentials()
    -            throws IOException, ServletException {
    -        TesterHttpServletRequest request = new TesterHttpServletRequest();
    -        request.setHeader(CorsFilter.REQUEST_HEADER_ORIGIN,
    -                TesterFilterConfigs.HTTPS_WWW_APACHE_ORG);
    -        request.setMethod("GET");
    -        TesterHttpServletResponse response = new TesterHttpServletResponse();
    -
    +    @Test(expected=ServletException.class)
    +    public void testDoFilterSimpleAnyOriginAndSupportsCredentials() throws ServletException {
             CorsFilter corsFilter = new CorsFilter();
    -        corsFilter.init(TesterFilterConfigs
    -                .getFilterConfigAnyOriginAndSupportsCredentials());
    -        corsFilter.doFilter(request, response, filterChain);
    -
    -        Assert.assertTrue(response.getHeader(
    -                CorsFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN).equals(
    -                TesterFilterConfigs.HTTPS_WWW_APACHE_ORG));
    -        Assert.assertTrue(response.getHeader(
    -                CorsFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_CREDENTIALS)
    -                .equals(
    -                        "true"));
    -        Assert.assertTrue(((Boolean) request.getAttribute(
    -                CorsFilter.HTTP_REQUEST_ATTRIBUTE_IS_CORS_REQUEST)).booleanValue());
    -        Assert.assertTrue(request.getAttribute(
    -                CorsFilter.HTTP_REQUEST_ATTRIBUTE_ORIGIN).equals(
    -                TesterFilterConfigs.HTTPS_WWW_APACHE_ORG));
    -        Assert.assertTrue(request.getAttribute(
    -                CorsFilter.HTTP_REQUEST_ATTRIBUTE_REQUEST_TYPE).equals(
    -                CorsFilter.CORSRequestType.SIMPLE.name().toLowerCase(Locale.ENGLISH)));
    +        corsFilter.init(TesterFilterConfigs.getFilterConfigAnyOriginAndSupportsCredentials());
         }
     
         /*
    @@ -261,8 +232,7 @@ public void testDoFilterSimpleWithExposedHeaders() throws IOException,
             corsFilter.doFilter(request, response, filterChain);
     
             Assert.assertTrue(response.getHeader(
    -                CorsFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN).equals(
    -                "https://www.apache.org"));
    +                CorsFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN).equals("*"));
             Assert.assertTrue(response.getHeader(
                     CorsFilter.RESPONSE_HEADER_ACCESS_CONTROL_EXPOSE_HEADERS)
                     .equals(TesterFilterConfigs.EXPOSED_HEADERS));
    @@ -727,9 +697,8 @@ public String getFilterName() {
             });
             corsFilter.doFilter(request, response, filterChain);
     
    -        Assert.assertTrue(response.getHeader(
    -                CorsFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN).equals(
    -                "https://www.apache.org"));
    +        Assert.assertNull(response.getHeader(
    +                CorsFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN));
             Assert.assertTrue(((Boolean) request.getAttribute(
                     CorsFilter.HTTP_REQUEST_ATTRIBUTE_IS_CORS_REQUEST)).booleanValue());
             Assert.assertTrue(request.getAttribute(
    @@ -1412,7 +1381,7 @@ public void testWithFilterConfig() throws ServletException {
             Assert.assertTrue(corsFilter.getAllowedOrigins().size() == 0);
             Assert.assertTrue(corsFilter.isAnyOriginAllowed());
             Assert.assertTrue(corsFilter.getExposedHeaders().size() == 0);
    -        Assert.assertTrue(corsFilter.isSupportsCredentials());
    +        Assert.assertFalse(corsFilter.isSupportsCredentials());
             Assert.assertTrue(corsFilter.getPreflightMaxAge() == 1800);
         }
     
    @@ -1448,9 +1417,9 @@ public void testWithStringParserNull() throws ServletException {
             Assert.assertTrue(corsFilter.getAllowedHttpHeaders().size() == 6);
             Assert.assertTrue(corsFilter.getAllowedHttpMethods().size() == 4);
             Assert.assertTrue(corsFilter.getAllowedOrigins().size() == 0);
    -        Assert.assertTrue(corsFilter.isAnyOriginAllowed());
    +        Assert.assertFalse(corsFilter.isAnyOriginAllowed());
             Assert.assertTrue(corsFilter.getExposedHeaders().size() == 0);
    -        Assert.assertTrue(corsFilter.isSupportsCredentials());
    +        Assert.assertFalse(corsFilter.isSupportsCredentials());
             Assert.assertTrue(corsFilter.getPreflightMaxAge() == 1800);
         }
     
    @@ -1554,8 +1523,7 @@ public void testDecorateRequestDisabled() throws IOException,
             corsFilter.doFilter(request, response, filterChain);
     
             Assert.assertTrue(response.getHeader(
    -                CorsFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN).equals(
    -                "https://www.apache.org"));
    +                CorsFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN).equals("*"));
             Assert.assertNull(request
                     .getAttribute(CorsFilter.HTTP_REQUEST_ATTRIBUTE_IS_CORS_REQUEST));
             Assert.assertNull(request
    
  • test/org/apache/catalina/filters/TesterFilterConfigs.java+6 5 modified
    @@ -36,12 +36,13 @@ public class TesterFilterConfigs {
         public static final TesterServletContext mockServletContext =
                 new TesterServletContext();
     
    +    // Default config for the test is to allow any origin
         public static FilterConfig getDefaultFilterConfig() {
             final String allowedHttpHeaders =
                     CorsFilter.DEFAULT_ALLOWED_HTTP_HEADERS;
             final String allowedHttpMethods =
                     CorsFilter.DEFAULT_ALLOWED_HTTP_METHODS;
    -        final String allowedOrigins = CorsFilter.DEFAULT_ALLOWED_ORIGINS;
    +        final String allowedOrigins = ANY_ORIGIN;
             final String exposedHeaders = CorsFilter.DEFAULT_EXPOSED_HEADERS;
             final String supportCredentials =
                     CorsFilter.DEFAULT_SUPPORTS_CREDENTIALS;
    @@ -59,7 +60,7 @@ public static FilterConfig getFilterConfigAnyOriginAndSupportsCredentials() {
                     CorsFilter.DEFAULT_ALLOWED_HTTP_HEADERS;
             final String allowedHttpMethods =
                     CorsFilter.DEFAULT_ALLOWED_HTTP_METHODS + ",PUT";
    -        final String allowedOrigins = CorsFilter.DEFAULT_ALLOWED_ORIGINS;
    +        final String allowedOrigins = ANY_ORIGIN;
             final String exposedHeaders = CorsFilter.DEFAULT_EXPOSED_HEADERS;
             final String supportCredentials = "true";
             final String preflightMaxAge =
    @@ -77,7 +78,7 @@ public static FilterConfig getFilterConfigAnyOriginAndSupportsCredentials() {
                     CorsFilter.DEFAULT_ALLOWED_HTTP_HEADERS;
             final String allowedHttpMethods =
                     CorsFilter.DEFAULT_ALLOWED_HTTP_METHODS + ",PUT";
    -        final String allowedOrigins = CorsFilter.DEFAULT_ALLOWED_ORIGINS;
    +        final String allowedOrigins = ANY_ORIGIN;
             final String exposedHeaders = CorsFilter.DEFAULT_EXPOSED_HEADERS;
             final String supportCredentials = "false";
             final String preflightMaxAge =
    @@ -131,7 +132,7 @@ public static FilterConfig getFilterConfigWithExposedHeaders() {
                     CorsFilter.DEFAULT_ALLOWED_HTTP_HEADERS;
             final String allowedHttpMethods =
                     CorsFilter.DEFAULT_ALLOWED_HTTP_METHODS;
    -        final String allowedOrigins = CorsFilter.DEFAULT_ALLOWED_ORIGINS;
    +        final String allowedOrigins = ANY_ORIGIN;
             final String exposedHeaders = EXPOSED_HEADERS;
             final String supportCredentials =
                     CorsFilter.DEFAULT_SUPPORTS_CREDENTIALS;
    @@ -240,7 +241,7 @@ public static FilterConfig getFilterConfigDecorateRequestDisabled() {
                     CorsFilter.DEFAULT_ALLOWED_HTTP_HEADERS;
             final String allowedHttpMethods =
                     CorsFilter.DEFAULT_ALLOWED_HTTP_METHODS;
    -        final String allowedOrigins = CorsFilter.DEFAULT_ALLOWED_ORIGINS;
    +        final String allowedOrigins = ANY_ORIGIN;
             final String exposedHeaders = CorsFilter.DEFAULT_EXPOSED_HEADERS;
             final String supportCredentials =
                     CorsFilter.DEFAULT_SUPPORTS_CREDENTIALS;
    
  • webapps/docs/changelog.xml+4 0 modified
    @@ -92,6 +92,10 @@
             usually performed during web application stop if that stop is triggered
             by a JVM shutdown. (markt)
           </fix>
    +      <fix>
    +        <bug>62343</bug>: Make CORS filter defaults more secure. This is the fix
    +        for CVE-2018-8014. (markt)
    +      </fix>
         </changelog>
       </subsection>
       <subsection name="Coyote">
    

Vulnerability mechanics

Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

68

News mentions

0

No linked articles in our index yet.