diff --git a/spring-web/src/main/java/org/springframework/web/bind/annotation/CrossOrigin.java b/spring-web/src/main/java/org/springframework/web/bind/annotation/CrossOrigin.java index 34a061cd0d..2614dc8c5d 100644 --- a/spring-web/src/main/java/org/springframework/web/bind/annotation/CrossOrigin.java +++ b/spring-web/src/main/java/org/springframework/web/bind/annotation/CrossOrigin.java @@ -28,7 +28,7 @@ import org.springframework.web.cors.CorsConfiguration; /** * Marks the annotated method or type as permitting cross origin requests. * - *

By default all origins and headers are permitted, credentials are allowed, + *

By default all origins and headers are permitted, credentials are not allowed, * and the maximum age is set to 1800 seconds (30 minutes). The list of HTTP * methods is set to the methods on the {@code @RequestMapping} if not * explicitly set on {@code @CrossOrigin}. @@ -64,7 +64,7 @@ public @interface CrossOrigin { * @deprecated as of Spring 5.0, in favor of using {@link CorsConfiguration#applyPermitDefaultValues} */ @Deprecated - boolean DEFAULT_ALLOW_CREDENTIALS = true; + boolean DEFAULT_ALLOW_CREDENTIALS = false; /** * @deprecated as of Spring 5.0, in favor of using {@link CorsConfiguration#applyPermitDefaultValues} @@ -118,12 +118,13 @@ public @interface CrossOrigin { /** * Whether the browser should include any cookies associated with the - * domain of the request being annotated. - *

Set to {@code "false"} if such cookies should not included. - * An empty string ({@code ""}) means undefined. - * {@code "true"} means that the pre-flight response will include the header - * {@code Access-Control-Allow-Credentials=true}. - *

If undefined, credentials are allowed. + * domain of the request being annotated. Be aware that enabling this option could + * increase the surface attack of the web application (for example via exposing + * sensitive user-specific information like CSRF tokens). + *

Set to {@code "true"} means that the pre-flight response will include the header + * {@code Access-Control-Allow-Credentials=true} so such cookies should be included. + *

If undefined or set to {@code "false"}, such header is not included and + * credentials are not allowed. */ String allowCredentials() default ""; diff --git a/spring-web/src/main/java/org/springframework/web/cors/CorsConfiguration.java b/spring-web/src/main/java/org/springframework/web/cors/CorsConfiguration.java index ddf101ce33..9cb065465c 100644 --- a/spring-web/src/main/java/org/springframework/web/cors/CorsConfiguration.java +++ b/spring-web/src/main/java/org/springframework/web/cors/CorsConfiguration.java @@ -324,7 +324,6 @@ public class CorsConfiguration { *

  • Allow all origins, i.e. {@code "*"}.
  • *
  • Allow "simple" methods {@code GET}, {@code HEAD} and {@code POST}.
  • *
  • Allow all headers.
  • - *
  • Allow credentials.
  • *
  • Set max age to 1800 seconds (30 minutes).
  • * */ @@ -339,9 +338,6 @@ public class CorsConfiguration { if (this.allowedHeaders == null) { this.addAllowedHeader(ALL); } - if (this.allowCredentials == null) { - this.setAllowCredentials(true); - } if (this.maxAge == null) { this.setMaxAge(1800L); } diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/config/CorsRegistration.java b/spring-webflux/src/main/java/org/springframework/web/reactive/config/CorsRegistration.java index 33bfca0b61..e9ceab271c 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/config/CorsRegistration.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/config/CorsRegistration.java @@ -113,9 +113,10 @@ public class CorsRegistration { } /** - * Whether user credentials are supported. - *

    By default this is set to {@code true} in which case user credentials - * are supported. + * Whether user credentials are supported. Be aware that enabling this option + * could increase the surface attack of the web application (for example via + * exposing sensitive user-specific information like CSRF tokens). + *

    By default credentials are not allowed. */ public CorsRegistration allowCredentials(boolean allowCredentials) { this.config.setAllowCredentials(allowCredentials); diff --git a/spring-webflux/src/test/java/org/springframework/web/reactive/result/method/annotation/CrossOriginAnnotationIntegrationTests.java b/spring-webflux/src/test/java/org/springframework/web/reactive/result/method/annotation/CrossOriginAnnotationIntegrationTests.java index 46d976b574..bdb7cb90db 100644 --- a/spring-webflux/src/test/java/org/springframework/web/reactive/result/method/annotation/CrossOriginAnnotationIntegrationTests.java +++ b/spring-webflux/src/test/java/org/springframework/web/reactive/result/method/annotation/CrossOriginAnnotationIntegrationTests.java @@ -104,8 +104,8 @@ public class CrossOriginAnnotationIntegrationTests extends AbstractRequestMappin public void actualRequestWithDefaultAnnotation() throws Exception { ResponseEntity entity = performGet("/default", this.headers, String.class); assertEquals(HttpStatus.OK, entity.getStatusCode()); - assertEquals("http://site1.com", entity.getHeaders().getAccessControlAllowOrigin()); - assertEquals(true, entity.getHeaders().getAccessControlAllowCredentials()); + assertEquals("*", entity.getHeaders().getAccessControlAllowOrigin()); + assertEquals(false, entity.getHeaders().getAccessControlAllowCredentials()); assertEquals("default", entity.getBody()); } @@ -114,9 +114,9 @@ public class CrossOriginAnnotationIntegrationTests extends AbstractRequestMappin this.headers.add(HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, "GET"); ResponseEntity entity = performOptions("/default", this.headers, Void.class); assertEquals(HttpStatus.OK, entity.getStatusCode()); - assertEquals("http://site1.com", entity.getHeaders().getAccessControlAllowOrigin()); + assertEquals("*", entity.getHeaders().getAccessControlAllowOrigin()); assertEquals(1800, entity.getHeaders().getAccessControlMaxAge()); - assertEquals(true, entity.getHeaders().getAccessControlAllowCredentials()); + assertEquals(false, entity.getHeaders().getAccessControlAllowCredentials()); } @Test diff --git a/spring-webflux/src/test/java/org/springframework/web/reactive/result/method/annotation/GlobalCorsConfigIntegrationTests.java b/spring-webflux/src/test/java/org/springframework/web/reactive/result/method/annotation/GlobalCorsConfigIntegrationTests.java index 39939d29e1..4a4a8af482 100644 --- a/spring-webflux/src/test/java/org/springframework/web/reactive/result/method/annotation/GlobalCorsConfigIntegrationTests.java +++ b/spring-webflux/src/test/java/org/springframework/web/reactive/result/method/annotation/GlobalCorsConfigIntegrationTests.java @@ -78,7 +78,7 @@ public class GlobalCorsConfigIntegrationTests extends AbstractRequestMappingInte public void actualRequestWithCorsEnabled() throws Exception { ResponseEntity entity = performGet("/cors", this.headers, String.class); assertEquals(HttpStatus.OK, entity.getStatusCode()); - assertEquals("http://localhost:9000", entity.getHeaders().getAccessControlAllowOrigin()); + assertEquals("*", entity.getHeaders().getAccessControlAllowOrigin()); assertEquals("cors", entity.getBody()); } @@ -106,7 +106,7 @@ public class GlobalCorsConfigIntegrationTests extends AbstractRequestMappingInte this.headers.add(HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, "GET"); ResponseEntity entity = performOptions("/cors", this.headers, String.class); assertEquals(HttpStatus.OK, entity.getStatusCode()); - assertEquals("http://localhost:9000", entity.getHeaders().getAccessControlAllowOrigin()); + assertEquals("*", entity.getHeaders().getAccessControlAllowOrigin()); } @Test diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/CorsRegistration.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/CorsRegistration.java index 4367c31719..1b0cab2d3d 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/CorsRegistration.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/CorsRegistration.java @@ -115,9 +115,10 @@ public class CorsRegistration { } /** - * Whether user credentials are supported. - *

    By default this is set to {@code true} in which case user credentials - * are supported. + * Whether user credentials are supported. Be aware that enabling this option + * could increase the surface attack of the web application (for example via + * exposing sensitive user-specific information like CSRF tokens). + *

    By default credentials are not allowed. */ public CorsRegistration allowCredentials(boolean allowCredentials) { this.config.setAllowCredentials(allowCredentials); diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/config/MvcNamespaceTests.java b/spring-webmvc/src/test/java/org/springframework/web/servlet/config/MvcNamespaceTests.java index 87c219e741..82bbcd1618 100644 --- a/spring-webmvc/src/test/java/org/springframework/web/servlet/config/MvcNamespaceTests.java +++ b/spring-webmvc/src/test/java/org/springframework/web/servlet/config/MvcNamespaceTests.java @@ -903,7 +903,7 @@ public class MvcNamespaceTests { assertArrayEquals(new String[]{"GET", "HEAD", "POST"}, config.getAllowedMethods().toArray()); assertArrayEquals(new String[]{"*"}, config.getAllowedHeaders().toArray()); assertNull(config.getExposedHeaders()); - assertTrue(config.getAllowCredentials()); + assertNull(config.getAllowCredentials()); assertEquals(Long.valueOf(1800), config.getMaxAge()); } } @@ -933,7 +933,7 @@ public class MvcNamespaceTests { assertArrayEquals(new String[]{"GET", "HEAD", "POST"}, config.getAllowedMethods().toArray()); assertArrayEquals(new String[]{"*"}, config.getAllowedHeaders().toArray()); assertNull(config.getExposedHeaders()); - assertTrue(config.getAllowCredentials()); + assertNull(config.getAllowCredentials()); assertEquals(Long.valueOf(1800), config.getMaxAge()); } } diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/CrossOriginTests.java b/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/CrossOriginTests.java index de41d5a439..bf82711b24 100644 --- a/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/CrossOriginTests.java +++ b/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/CrossOriginTests.java @@ -126,7 +126,7 @@ public class CrossOriginTests { assertNotNull(config); assertArrayEquals(new String[] {"GET"}, config.getAllowedMethods().toArray()); assertArrayEquals(new String[] {"*"}, config.getAllowedOrigins().toArray()); - assertTrue(config.getAllowCredentials()); + assertNull(config.getAllowCredentials()); assertArrayEquals(new String[] {"*"}, config.getAllowedHeaders().toArray()); assertTrue(CollectionUtils.isEmpty(config.getExposedHeaders())); assertEquals(new Long(1800), config.getMaxAge()); @@ -155,7 +155,7 @@ public class CrossOriginTests { CorsConfiguration config = getCorsConfiguration(chain, false); assertNotNull(config); assertEquals(Arrays.asList("http://example.com"), config.getAllowedOrigins()); - assertTrue(config.getAllowCredentials()); + assertNull(config.getAllowCredentials()); } @Test @@ -166,7 +166,7 @@ public class CrossOriginTests { CorsConfiguration config = getCorsConfiguration(chain, false); assertNotNull(config); assertEquals(Arrays.asList("http://example.com"), config.getAllowedOrigins()); - assertTrue(config.getAllowCredentials()); + assertNull(config.getAllowCredentials()); } @Test @@ -243,7 +243,7 @@ public class CrossOriginTests { assertNotNull(config); assertArrayEquals(new String[] {"GET"}, config.getAllowedMethods().toArray()); assertArrayEquals(new String[] {"*"}, config.getAllowedOrigins().toArray()); - assertTrue(config.getAllowCredentials()); + assertNull(config.getAllowCredentials()); assertArrayEquals(new String[] {"*"}, config.getAllowedHeaders().toArray()); assertTrue(CollectionUtils.isEmpty(config.getExposedHeaders())); assertEquals(new Long(1800), config.getMaxAge()); diff --git a/src/docs/asciidoc/web/webflux-cors.adoc b/src/docs/asciidoc/web/webflux-cors.adoc index 9dcbbdaced..24f1f70556 100644 --- a/src/docs/asciidoc/web/webflux-cors.adoc +++ b/src/docs/asciidoc/web/webflux-cors.adoc @@ -24,6 +24,13 @@ implementation (https://github.com/spring-projects/spring-framework/blob/master/ by default) in order to add the relevant CORS response headers (like `Access-Control-Allow-Origin`) based on the CORS configuration you have provided. +[NOTE] +==== +Be aware that cookies are not allowed by default to avoid increasing the surface attack of +the web application (for example via exposing sensitive user-specific information like +CSRF tokens). Set `allowedCredentials` property to `true` in order to allow them. +==== + [[webflux-cors-controller]] == @CrossOrigin @@ -146,7 +153,7 @@ public class WebConfig implements WebFluxConfigurer { .allowedMethods("PUT", "DELETE") .allowedHeaders("header1", "header2", "header3") .exposedHeaders("header1", "header2") - .allowCredentials(false).maxAge(3600); + .allowCredentials(true).maxAge(3600); } } ---- diff --git a/src/docs/asciidoc/web/webmvc-cors.adoc b/src/docs/asciidoc/web/webmvc-cors.adoc index dc77b0d226..1b3aa0021f 100644 --- a/src/docs/asciidoc/web/webmvc-cors.adoc +++ b/src/docs/asciidoc/web/webmvc-cors.adoc @@ -27,6 +27,13 @@ implementation (https://github.com/spring-projects/spring-framework/blob/master/ by default) in order to add the relevant CORS response headers (like `Access-Control-Allow-Origin`) based on the CORS configuration you have provided. +[NOTE] +==== +Be aware that cookies are not allowed by default to avoid increasing the surface attack of +the web application (for example via exposing sensitive user-specific information like +CSRF tokens). Set `allowedCredentials` property to `true` in order to allow them. +==== + [NOTE] ==== Since CORS requests are automatically dispatched, you *do not need* to change the @@ -165,7 +172,7 @@ public class WebConfig implements WebMvcConfigurer { .allowedMethods("PUT", "DELETE") .allowedHeaders("header1", "header2", "header3") .exposedHeaders("header1", "header2") - .allowCredentials(false).maxAge(3600); + .allowCredentials(true).maxAge(3600); } } ---- @@ -197,7 +204,7 @@ It is also possible to declare several CORS mappings with customized properties: allowed-origins="http://domain1.com, http://domain2.com" allowed-methods="GET, PUT" allowed-headers="header1, header2, header3" - exposed-headers="header1, header2" allow-credentials="false" + exposed-headers="header1, header2" allow-credentials="true" max-age="123" />