Disable CORS credentials by default

Access-Control-Allow-Credentials CORS header, used to
allow cookies with CORS requests, is not set to true
anymore by default when enabling CORS with
@CrossOrigin or global CORS configuration in order to
provide a more secured default CORS configuration.

The related allowCredentials property now requires to
be set to true explicitly in order to support cookies
with CORS requests.

Issue: SPR-16130
This commit is contained in:
sdeleuze 2017-11-22 11:38:55 +01:00
parent 93f17dae47
commit 652e5c5584
10 changed files with 46 additions and 33 deletions

View File

@ -28,7 +28,7 @@ import org.springframework.web.cors.CorsConfiguration;
/**
* Marks the annotated method or type as permitting cross origin requests.
*
* <p>By default all origins and headers are permitted, credentials are allowed,
* <p>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.
* <p>Set to {@code "false"} if such cookies should not included.
* An empty string ({@code ""}) means <em>undefined</em>.
* {@code "true"} means that the pre-flight response will include the header
* {@code Access-Control-Allow-Credentials=true}.
* <p>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).
* <p>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.
* <p>If undefined or set to {@code "false"}, such header is not included and
* credentials are not allowed.
*/
String allowCredentials() default "";

View File

@ -324,7 +324,6 @@ public class CorsConfiguration {
* <li>Allow all origins, i.e. {@code "*"}.</li>
* <li>Allow "simple" methods {@code GET}, {@code HEAD} and {@code POST}.</li>
* <li>Allow all headers.</li>
* <li>Allow credentials.</li>
* <li>Set max age to 1800 seconds (30 minutes).</li>
* </ul>
*/
@ -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);
}

View File

@ -113,9 +113,10 @@ public class CorsRegistration {
}
/**
* Whether user credentials are supported.
* <p>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).
* <p>By default credentials are not allowed.
*/
public CorsRegistration allowCredentials(boolean allowCredentials) {
this.config.setAllowCredentials(allowCredentials);

View File

@ -104,8 +104,8 @@ public class CrossOriginAnnotationIntegrationTests extends AbstractRequestMappin
public void actualRequestWithDefaultAnnotation() throws Exception {
ResponseEntity<String> 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<Void> 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

View File

@ -78,7 +78,7 @@ public class GlobalCorsConfigIntegrationTests extends AbstractRequestMappingInte
public void actualRequestWithCorsEnabled() throws Exception {
ResponseEntity<String> 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<String> entity = performOptions("/cors", this.headers, String.class);
assertEquals(HttpStatus.OK, entity.getStatusCode());
assertEquals("http://localhost:9000", entity.getHeaders().getAccessControlAllowOrigin());
assertEquals("*", entity.getHeaders().getAccessControlAllowOrigin());
}
@Test

View File

@ -115,9 +115,10 @@ public class CorsRegistration {
}
/**
* Whether user credentials are supported.
* <p>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).
* <p>By default credentials are not allowed.
*/
public CorsRegistration allowCredentials(boolean allowCredentials) {
this.config.setAllowCredentials(allowCredentials);

View File

@ -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());
}
}

View File

@ -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());

View File

@ -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);
}
}
----

View File

@ -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" />
<mvc:mapping path="/resources/**"