mirror of
				https://github.com/spring-projects/spring-security.git
				synced 2025-10-30 22:28:46 +00:00 
			
		
		
		
	Default X-Xss-Protection header value to "0"
Closes gh-9631
This commit is contained in:
		
							parent
							
								
									dcda899c8c
								
							
						
					
					
						commit
						27059ced87
					
				| @ -64,7 +64,7 @@ import org.springframework.util.Assert; | ||||
|  * X-Content-Type-Options: nosniff | ||||
|  * Strict-Transport-Security: max-age=31536000 ; includeSubDomains | ||||
|  * X-Frame-Options: DENY | ||||
|  * X-XSS-Protection: 1; mode=block | ||||
|  * X-XSS-Protection: 0 | ||||
|  * </pre> | ||||
|  * | ||||
|  * @author Rob Winch | ||||
| @ -73,6 +73,7 @@ import org.springframework.util.Assert; | ||||
|  * @author Eddú Meléndez | ||||
|  * @author Vedran Pavic | ||||
|  * @author Ankur Pathak | ||||
|  * @author Daniel Garnier-Moiroux | ||||
|  * @since 3.2 | ||||
|  */ | ||||
| public class HeadersConfigurer<H extends HttpSecurityBuilder<H>> | ||||
| @ -733,50 +734,6 @@ public class HeadersConfigurer<H extends HttpSecurityBuilder<H>> | ||||
| 			enable(); | ||||
| 		} | ||||
| 
 | ||||
| 		/** | ||||
| 		 * If false, will not specify the mode as blocked. In this instance, any content | ||||
| 		 * will be attempted to be fixed. If true, the content will be replaced with "#". | ||||
| 		 * @param enabled the new value | ||||
| 		 * @deprecated use | ||||
| 		 * {@link XXssConfig#headerValue(XXssProtectionHeaderWriter.HeaderValue)} instead | ||||
| 		 */ | ||||
| 		@Deprecated | ||||
| 		public XXssConfig block(boolean enabled) { | ||||
| 			this.writer.setBlock(enabled); | ||||
| 			return this; | ||||
| 		} | ||||
| 
 | ||||
| 		/** | ||||
| 		 * If true, the header value will contain a value of 1. For example: | ||||
| 		 * | ||||
| 		 * <pre> | ||||
| 		 * X-XSS-Protection: 1 | ||||
| 		 * </pre> | ||||
| 		 * | ||||
| 		 * or if {@link XXssProtectionHeaderWriter#setBlock(boolean)} of the given | ||||
| 		 * {@link XXssProtectionHeaderWriter} is true | ||||
| 		 * | ||||
| 		 * | ||||
| 		 * <pre> | ||||
| 		 * X-XSS-Protection: 1; mode=block | ||||
| 		 * </pre> | ||||
| 		 * | ||||
| 		 * If false, will explicitly disable specify that X-XSS-Protection is disabled. | ||||
| 		 * For example: | ||||
| 		 * | ||||
| 		 * <pre> | ||||
| 		 * X-XSS-Protection: 0 | ||||
| 		 * </pre> | ||||
| 		 * @param enabled the new value | ||||
| 		 * @deprecated use | ||||
| 		 * {@link XXssConfig#headerValue(XXssProtectionHeaderWriter.HeaderValue)} instead | ||||
| 		 */ | ||||
| 		@Deprecated | ||||
| 		public XXssConfig xssProtectionEnabled(boolean enabled) { | ||||
| 			this.writer.setEnabled(enabled); | ||||
| 			return this; | ||||
| 		} | ||||
| 
 | ||||
| 		/** | ||||
| 		 * Sets the value of the X-XSS-PROTECTION header. OWASP recommends using | ||||
| 		 * {@link XXssProtectionHeaderWriter.HeaderValue#DISABLED}. | ||||
|  | ||||
| @ -69,10 +69,6 @@ public class HeadersBeanDefinitionParser implements BeanDefinitionParser { | ||||
| 
 | ||||
| 	private static final String ATT_DISABLED = "disabled"; | ||||
| 
 | ||||
| 	private static final String ATT_ENABLED = "enabled"; | ||||
| 
 | ||||
| 	private static final String ATT_BLOCK = "block"; | ||||
| 
 | ||||
| 	private static final String ATT_POLICY = "policy"; | ||||
| 
 | ||||
| 	private static final String ATT_STRATEGY = "strategy"; | ||||
| @ -583,20 +579,6 @@ public class HeadersBeanDefinitionParser implements BeanDefinitionParser { | ||||
| 		BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(XXssProtectionHeaderWriter.class); | ||||
| 		if (xssElt != null) { | ||||
| 			boolean disabled = "true".equals(getAttribute(xssElt, ATT_DISABLED, "false")); | ||||
| 			String enabled = xssElt.getAttribute(ATT_ENABLED); | ||||
| 			if (StringUtils.hasText(enabled)) { | ||||
| 				if (disabled) { | ||||
| 					attrNotAllowed(parserContext, ATT_ENABLED, ATT_DISABLED, xssElt); | ||||
| 				} | ||||
| 				builder.addPropertyValue("enabled", enabled); | ||||
| 			} | ||||
| 			String block = xssElt.getAttribute(ATT_BLOCK); | ||||
| 			if (StringUtils.hasText(block)) { | ||||
| 				if (disabled) { | ||||
| 					attrNotAllowed(parserContext, ATT_BLOCK, ATT_DISABLED, xssElt); | ||||
| 				} | ||||
| 				builder.addPropertyValue("block", block); | ||||
| 			} | ||||
| 			XXssProtectionHeaderWriter.HeaderValue headerValue = XXssProtectionHeaderWriter.HeaderValue | ||||
| 					.from(xssElt.getAttribute(ATT_HEADER_VALUE)); | ||||
| 			if (headerValue != null) { | ||||
|  | ||||
| @ -1040,7 +1040,7 @@ public class ServerHttpSecurity { | ||||
| 	 * X-Content-Type-Options: nosniff | ||||
| 	 * Strict-Transport-Security: max-age=31536000 ; includeSubDomains | ||||
| 	 * X-Frame-Options: DENY | ||||
| 	 * X-XSS-Protection: 1; mode=block | ||||
| 	 * X-XSS-Protection: 0 | ||||
| 	 * </pre> | ||||
| 	 * | ||||
| 	 * such that "Strict-Transport-Security" is only added on secure requests. | ||||
| @ -1081,7 +1081,7 @@ public class ServerHttpSecurity { | ||||
| 	 * X-Content-Type-Options: nosniff | ||||
| 	 * Strict-Transport-Security: max-age=31536000 ; includeSubDomains | ||||
| 	 * X-Frame-Options: DENY | ||||
| 	 * X-XSS-Protection: 1; mode=block | ||||
| 	 * X-XSS-Protection: 0 | ||||
| 	 * </pre> | ||||
| 	 * | ||||
| 	 * such that "Strict-Transport-Security" is only added on secure requests. | ||||
|  | ||||
| @ -25,18 +25,12 @@ import org.springframework.security.web.header.writers.XXssProtectionHeaderWrite | ||||
|  * idiomatic Kotlin code. | ||||
|  * | ||||
|  * @author Eleftheria Stein | ||||
|  * @author Daniel Garnier-Moiroux | ||||
|  * @since 5.3 | ||||
|  * @property block whether to specify the mode as blocked | ||||
|  * @property xssProtectionEnabled if true, the header value will contain a value of 1. | ||||
|  * If false, will explicitly disable specify that X-XSS-Protection is disabled. | ||||
|  * @property headerValue the value of the X-XSS-Protection header. OWASP recommends [HeaderValue.DISABLED]. | ||||
|  */ | ||||
| @HeadersSecurityMarker | ||||
| class XssProtectionConfigDsl { | ||||
|     @Deprecated("use headerValue instead") | ||||
|     var block: Boolean? = null | ||||
|     @Deprecated("use headerValue instead") | ||||
|     var xssProtectionEnabled: Boolean? = null | ||||
|     var headerValue: HeaderValue? = null | ||||
| 
 | ||||
|     private var disabled = false | ||||
| @ -50,8 +44,6 @@ class XssProtectionConfigDsl { | ||||
| 
 | ||||
|     internal fun get(): (HeadersConfigurer<HttpSecurity>.XXssConfig) -> Unit { | ||||
|         return { xssProtection -> | ||||
|             block?.also { xssProtection.block(block!!) } | ||||
|             xssProtectionEnabled?.also { xssProtection.xssProtectionEnabled(xssProtectionEnabled!!) } | ||||
|             headerValue?.also { xssProtection.headerValue(headerValue) } | ||||
| 
 | ||||
|             if (disabled) { | ||||
|  | ||||
| @ -1268,13 +1268,7 @@ xss-protection.attlist &= | ||||
| 	## disable the X-XSS-Protection header. Default is 'false' meaning it is enabled. | ||||
| 	attribute disabled {xsd:boolean}? | ||||
| xss-protection.attlist &= | ||||
| 	## specify that XSS Protection should be explicitly enabled or disabled. Default is 'true' meaning it is enabled. | ||||
| 	attribute enabled {xsd:boolean}? | ||||
| xss-protection.attlist &= | ||||
| 	## Add mode=block to the header or not, default is on. | ||||
| 	attribute block {xsd:boolean}? | ||||
| xss-protection.attlist &= | ||||
| 	## Specify the value for the X-Xss-Protection header. When set, overrides both enabled and block attributes. | ||||
| 	## Specify the value for the X-Xss-Protection header. Defaults to "0". | ||||
| 	attribute header-value {"0"|"1"|"1; mode=block"}? | ||||
| 
 | ||||
| content-type-options = | ||||
|  | ||||
| @ -3553,23 +3553,9 @@ | ||||
|                 </xs:documentation> | ||||
|          </xs:annotation> | ||||
|       </xs:attribute> | ||||
|       <xs:attribute name="enabled" type="xs:boolean"> | ||||
|          <xs:annotation> | ||||
|             <xs:documentation>specify that XSS Protection should be explicitly enabled or disabled. Default is 'true' | ||||
|                 meaning it is enabled. | ||||
|                 </xs:documentation> | ||||
|          </xs:annotation> | ||||
|       </xs:attribute> | ||||
|       <xs:attribute name="block" type="xs:boolean"> | ||||
|          <xs:annotation> | ||||
|             <xs:documentation>Add mode=block to the header or not, default is on. | ||||
|                 </xs:documentation> | ||||
|          </xs:annotation> | ||||
|       </xs:attribute> | ||||
|       <xs:attribute name="header-value"> | ||||
|          <xs:annotation> | ||||
|             <xs:documentation>Specify the value for the X-Xss-Protection header. When set, overrides both enabled and | ||||
|                 block attributes. | ||||
|             <xs:documentation>Specify the value for the X-Xss-Protection header. Defaults to "0". | ||||
|                 </xs:documentation> | ||||
|          </xs:annotation> | ||||
|          <xs:simpleType> | ||||
|  | ||||
| @ -122,7 +122,7 @@ public class HttpSecurityConfigurationTests { | ||||
| 				.andExpect(header().string(HttpHeaders.CACHE_CONTROL, "no-cache, no-store, max-age=0, must-revalidate")) | ||||
| 				.andExpect(header().string(HttpHeaders.EXPIRES, "0")) | ||||
| 				.andExpect(header().string(HttpHeaders.PRAGMA, "no-cache")) | ||||
| 				.andExpect(header().string(HttpHeaders.X_XSS_PROTECTION, "1; mode=block")) | ||||
| 				.andExpect(header().string(HttpHeaders.X_XSS_PROTECTION, "0")) | ||||
| 				.andReturn(); | ||||
| 		// @formatter:on | ||||
| 		assertThat(mvcResult.getResponse().getHeaderNames()).containsExactlyInAnyOrder( | ||||
|  | ||||
| @ -57,7 +57,7 @@ public class HeadersConfigurerEagerHeadersTests { | ||||
| 				.andExpect(header().string(HttpHeaders.CACHE_CONTROL, "no-cache, no-store, max-age=0, must-revalidate")) | ||||
| 				.andExpect(header().string(HttpHeaders.EXPIRES, "0")) | ||||
| 				.andExpect(header().string(HttpHeaders.PRAGMA, "no-cache")) | ||||
| 				.andExpect(header().string("X-XSS-Protection", "1; mode=block")); | ||||
| 				.andExpect(header().string("X-XSS-Protection", "0")); | ||||
| 	} | ||||
| 
 | ||||
| 	@Configuration | ||||
|  | ||||
| @ -80,7 +80,7 @@ public class HeadersConfigurerTests { | ||||
| 				.andExpect(header().string(HttpHeaders.CACHE_CONTROL, "no-cache, no-store, max-age=0, must-revalidate")) | ||||
| 				.andExpect(header().string(HttpHeaders.EXPIRES, "0")) | ||||
| 				.andExpect(header().string(HttpHeaders.PRAGMA, "no-cache")) | ||||
| 				.andExpect(header().string(HttpHeaders.X_XSS_PROTECTION, "1; mode=block")).andReturn(); | ||||
| 				.andExpect(header().string(HttpHeaders.X_XSS_PROTECTION, "0")).andReturn(); | ||||
| 		assertThat(mvcResult.getResponse().getHeaderNames()).containsExactlyInAnyOrder( | ||||
| 				HttpHeaders.X_CONTENT_TYPE_OPTIONS, HttpHeaders.X_FRAME_OPTIONS, HttpHeaders.STRICT_TRANSPORT_SECURITY, | ||||
| 				HttpHeaders.CACHE_CONTROL, HttpHeaders.EXPIRES, HttpHeaders.PRAGMA, HttpHeaders.X_XSS_PROTECTION); | ||||
| @ -97,7 +97,7 @@ public class HeadersConfigurerTests { | ||||
| 				.andExpect(header().string(HttpHeaders.CACHE_CONTROL, "no-cache, no-store, max-age=0, must-revalidate")) | ||||
| 				.andExpect(header().string(HttpHeaders.EXPIRES, "0")) | ||||
| 				.andExpect(header().string(HttpHeaders.PRAGMA, "no-cache")) | ||||
| 				.andExpect(header().string(HttpHeaders.X_XSS_PROTECTION, "1; mode=block")).andReturn(); | ||||
| 				.andExpect(header().string(HttpHeaders.X_XSS_PROTECTION, "0")).andReturn(); | ||||
| 		assertThat(mvcResult.getResponse().getHeaderNames()).containsExactlyInAnyOrder( | ||||
| 				HttpHeaders.X_CONTENT_TYPE_OPTIONS, HttpHeaders.X_FRAME_OPTIONS, HttpHeaders.STRICT_TRANSPORT_SECURITY, | ||||
| 				HttpHeaders.CACHE_CONTROL, HttpHeaders.EXPIRES, HttpHeaders.PRAGMA, HttpHeaders.X_XSS_PROTECTION); | ||||
| @ -169,16 +169,16 @@ public class HeadersConfigurerTests { | ||||
| 			throws Exception { | ||||
| 		this.spring.register(XssProtectionConfig.class).autowire(); | ||||
| 		MvcResult mvcResult = this.mvc.perform(get("/").secure(true)) | ||||
| 				.andExpect(header().string(HttpHeaders.X_XSS_PROTECTION, "1; mode=block")).andReturn(); | ||||
| 				.andExpect(header().string(HttpHeaders.X_XSS_PROTECTION, "0")).andReturn(); | ||||
| 		assertThat(mvcResult.getResponse().getHeaderNames()).containsExactly(HttpHeaders.X_XSS_PROTECTION); | ||||
| 	} | ||||
| 
 | ||||
| 	@Test | ||||
| 	public void getWhenHeaderDefaultsDisabledAndXssProtectionConfiguredValueDisabledThenOnlyXssProtectionHeaderInResponse() | ||||
| 	public void getWhenHeaderDefaultsDisabledAndXssProtectionConfiguredEnabledModeBlockThenOnlyXssProtectionHeaderInResponse() | ||||
| 			throws Exception { | ||||
| 		this.spring.register(XssProtectionValueDisabledConfig.class).autowire(); | ||||
| 		this.spring.register(XssProtectionValueEnabledModeBlockConfig.class).autowire(); | ||||
| 		MvcResult mvcResult = this.mvc.perform(get("/").secure(true)) | ||||
| 				.andExpect(header().string(HttpHeaders.X_XSS_PROTECTION, "0")).andReturn(); | ||||
| 				.andExpect(header().string(HttpHeaders.X_XSS_PROTECTION, "1; mode=block")).andReturn(); | ||||
| 		assertThat(mvcResult.getResponse().getHeaderNames()).containsExactly(HttpHeaders.X_XSS_PROTECTION); | ||||
| 	} | ||||
| 
 | ||||
| @ -186,16 +186,16 @@ public class HeadersConfigurerTests { | ||||
| 	public void getWhenOnlyXssProtectionConfiguredInLambdaThenOnlyXssProtectionHeaderInResponse() throws Exception { | ||||
| 		this.spring.register(XssProtectionInLambdaConfig.class).autowire(); | ||||
| 		MvcResult mvcResult = this.mvc.perform(get("/").secure(true)) | ||||
| 				.andExpect(header().string(HttpHeaders.X_XSS_PROTECTION, "1; mode=block")).andReturn(); | ||||
| 				.andExpect(header().string(HttpHeaders.X_XSS_PROTECTION, "0")).andReturn(); | ||||
| 		assertThat(mvcResult.getResponse().getHeaderNames()).containsExactly(HttpHeaders.X_XSS_PROTECTION); | ||||
| 	} | ||||
| 
 | ||||
| 	@Test | ||||
| 	public void getWhenHeaderDefaultsDisabledAndXssProtectionConfiguredValueDisabledInLambdaThenOnlyXssProtectionHeaderInResponse() | ||||
| 	public void getWhenHeaderDefaultsDisabledAndXssProtectionConfiguredValueEnabledModeBlockInLambdaThenOnlyXssProtectionHeaderInResponse() | ||||
| 			throws Exception { | ||||
| 		this.spring.register(XssProtectionValueDisabledInLambdaConfig.class).autowire(); | ||||
| 		this.spring.register(XssProtectionValueEnabledModeBlockInLambdaConfig.class).autowire(); | ||||
| 		MvcResult mvcResult = this.mvc.perform(get("/").secure(true)) | ||||
| 				.andExpect(header().string(HttpHeaders.X_XSS_PROTECTION, "0")).andReturn(); | ||||
| 				.andExpect(header().string(HttpHeaders.X_XSS_PROTECTION, "1; mode=block")).andReturn(); | ||||
| 		assertThat(mvcResult.getResponse().getHeaderNames()).containsExactly(HttpHeaders.X_XSS_PROTECTION); | ||||
| 	} | ||||
| 
 | ||||
| @ -719,7 +719,7 @@ public class HeadersConfigurerTests { | ||||
| 
 | ||||
| 	@Configuration | ||||
| 	@EnableWebSecurity | ||||
| 	static class XssProtectionValueDisabledConfig { | ||||
| 	static class XssProtectionValueEnabledModeBlockConfig { | ||||
| 
 | ||||
| 		@Bean | ||||
| 		SecurityFilterChain filterChain(HttpSecurity http) throws Exception { | ||||
| @ -728,7 +728,7 @@ public class HeadersConfigurerTests { | ||||
| 				.headers() | ||||
| 					.defaultsDisabled() | ||||
| 					.xssProtection() | ||||
| 					.headerValue(XXssProtectionHeaderWriter.HeaderValue.DISABLED); | ||||
| 					.headerValue(XXssProtectionHeaderWriter.HeaderValue.ENABLED_MODE_BLOCK); | ||||
| 			// @formatter:on | ||||
| 			return http.build(); | ||||
| 		} | ||||
| @ -755,7 +755,7 @@ public class HeadersConfigurerTests { | ||||
| 
 | ||||
| 	@Configuration | ||||
| 	@EnableWebSecurity | ||||
| 	static class XssProtectionValueDisabledInLambdaConfig { | ||||
| 	static class XssProtectionValueEnabledModeBlockInLambdaConfig { | ||||
| 
 | ||||
| 		@Bean | ||||
| 		SecurityFilterChain filterChain(HttpSecurity http) throws Exception { | ||||
| @ -765,7 +765,7 @@ public class HeadersConfigurerTests { | ||||
| 					headers | ||||
| 						.defaultsDisabled() | ||||
| 						.xssProtection((xXssConfig) -> | ||||
| 							xXssConfig.headerValue(XXssProtectionHeaderWriter.HeaderValue.DISABLED) | ||||
| 							xXssConfig.headerValue(XXssProtectionHeaderWriter.HeaderValue.ENABLED_MODE_BLOCK) | ||||
| 						) | ||||
| 				); | ||||
| 			// @formatter:on | ||||
|  | ||||
| @ -62,7 +62,7 @@ public class NamespaceHttpHeadersTests { | ||||
| 		defaultHeaders.put("Cache-Control", "no-cache, no-store, max-age=0, must-revalidate"); | ||||
| 		defaultHeaders.put("Expires", "0"); | ||||
| 		defaultHeaders.put("Pragma", "no-cache"); | ||||
| 		defaultHeaders.put("X-XSS-Protection", "1; mode=block"); | ||||
| 		defaultHeaders.put("X-XSS-Protection", "0"); | ||||
| 	} | ||||
| 	public final SpringTestContext spring = new SpringTestContext(this); | ||||
| 
 | ||||
| @ -116,7 +116,7 @@ public class NamespaceHttpHeadersTests { | ||||
| 	@Test | ||||
| 	public void requestWhenXssCustomThenBehaviorMatchesNamespace() throws Exception { | ||||
| 		this.spring.register(XssProtectionCustomConfig.class).autowire(); | ||||
| 		this.mvc.perform(get("/")).andExpect(includes(Collections.singletonMap("X-XSS-Protection", "1"))); | ||||
| 		this.mvc.perform(get("/")).andExpect(includes(Collections.singletonMap("X-XSS-Protection", "1; mode=block"))); | ||||
| 	} | ||||
| 
 | ||||
| 	@Test | ||||
| @ -291,7 +291,7 @@ public class NamespaceHttpHeadersTests { | ||||
| 					// xss-protection@enabled and xss-protection@block | ||||
| 					.defaultsDisabled() | ||||
| 					.xssProtection() | ||||
| 						.headerValue(XXssProtectionHeaderWriter.HeaderValue.ENABLED); | ||||
| 						.headerValue(XXssProtectionHeaderWriter.HeaderValue.ENABLED_MODE_BLOCK); | ||||
| 			// @formatter:on | ||||
| 			return http.build(); | ||||
| 		} | ||||
|  | ||||
| @ -62,7 +62,7 @@ public class HttpHeadersConfigTests { | ||||
| 			.put("Cache-Control", "no-cache, no-store, max-age=0, must-revalidate") | ||||
| 			.put("Expires", "0") | ||||
| 			.put("Pragma", "no-cache") | ||||
| 			.put("X-XSS-Protection", "1; mode=block") | ||||
| 			.put("X-XSS-Protection", "0") | ||||
| 			.build(); | ||||
| 	// @formatter:on | ||||
| 
 | ||||
| @ -351,32 +351,6 @@ public class HttpHeadersConfigTests { | ||||
| 		excludedHeaders.remove("X-XSS-Protection"); | ||||
| 		this.spring.configLocations(this.xml("DefaultsDisabledWithXssProtection")).autowire(); | ||||
| 		// @formatter:off | ||||
| 		this.mvc.perform(get("/")) | ||||
| 				.andExpect(status().isOk()) | ||||
| 				.andExpect(header().string("X-XSS-Protection", "1; mode=block")) | ||||
| 				.andExpect(excludes(excludedHeaders)); | ||||
| 		// @formatter:on | ||||
| 	} | ||||
| 
 | ||||
| 	@Test | ||||
| 	public void requestWhenEnablingXssProtectionThenDefaultsToModeBlock() throws Exception { | ||||
| 		Set<String> excludedHeaders = new HashSet<>(defaultHeaders.keySet()); | ||||
| 		excludedHeaders.remove("X-XSS-Protection"); | ||||
| 		this.spring.configLocations(this.xml("DefaultsDisabledWithXssProtectionEnabled")).autowire(); | ||||
| 		// @formatter:off | ||||
| 		this.mvc.perform(get("/")) | ||||
| 				.andExpect(status().isOk()) | ||||
| 				.andExpect(header().string("X-XSS-Protection", "1; mode=block")) | ||||
| 				.andExpect(excludes(excludedHeaders)); | ||||
| 		// @formatter:on | ||||
| 	} | ||||
| 
 | ||||
| 	@Test | ||||
| 	public void requestWhenDisablingXssProtectionThenDefaultsToZero() throws Exception { | ||||
| 		Set<String> excludedHeaders = new HashSet<>(defaultHeaders.keySet()); | ||||
| 		excludedHeaders.remove("X-XSS-Protection"); | ||||
| 		this.spring.configLocations(this.xml("DefaultsDisabledWithXssProtectionDisabled")).autowire(); | ||||
| 		// @formatter:off | ||||
| 		this.mvc.perform(get("/")) | ||||
| 				.andExpect(status().isOk()) | ||||
| 				.andExpect(header().string("X-XSS-Protection", "0")) | ||||
| @ -423,33 +397,6 @@ public class HttpHeadersConfigTests { | ||||
| 		// @formatter:on | ||||
| 	} | ||||
| 
 | ||||
| 	@Test | ||||
| 	public void requestWhenSettingXssProtectionDisabledHeaderValueToOneThenDefaultsToOne() throws Exception { | ||||
| 		Set<String> excludedHeaders = new HashSet<>(defaultHeaders.keySet()); | ||||
| 		excludedHeaders.remove("X-XSS-Protection"); | ||||
| 		this.spring.configLocations(this.xml("DefaultsDisabledWithXssProtectionDisabledAndHeaderValueOne")).autowire(); | ||||
| 		// @formatter:off | ||||
| 		this.mvc.perform(get("/")) | ||||
| 				.andExpect(status().isOk()) | ||||
| 				.andExpect(header().string("X-XSS-Protection", "1")) | ||||
| 				.andExpect(excludes(excludedHeaders)); | ||||
| 		// @formatter:on | ||||
| 	} | ||||
| 
 | ||||
| 	@Test | ||||
| 	public void configureWhenXssProtectionDisabledAndBlockSetThenAutowireFails() { | ||||
| 		/* | ||||
| 		 * NOTE: Original error message "Cannot set block to true with enabled false" no | ||||
| 		 * longer shows up in stack trace as of Spring Framework 6.x. | ||||
| 		 * | ||||
| 		 * See https://github.com/spring-projects/spring-framework/issues/25162. | ||||
| 		 */ | ||||
| 		assertThatExceptionOfType(BeanCreationException.class) | ||||
| 				.isThrownBy(() -> this.spring | ||||
| 						.configLocations(this.xml("DefaultsDisabledWithXssProtectionDisabledAndBlockSet")).autowire()) | ||||
| 				.havingRootCause().withMessageContaining("Property 'block' threw exception"); | ||||
| 	} | ||||
| 
 | ||||
| 	@Test | ||||
| 	public void requestWhenUsingCacheControlThenRespondsWithCorrespondingHeaders() throws Exception { | ||||
| 		Map<String, String> includedHeaders = ImmutableMap.<String, String>builder() | ||||
| @ -693,21 +640,6 @@ public class HttpHeadersConfigTests { | ||||
| 				.withMessageContaining("request-matcher-ref"); | ||||
| 	} | ||||
| 
 | ||||
| 	@Test | ||||
| 	public void configureWhenXssProtectionDisabledAndEnabledThenAutowireFails() { | ||||
| 		assertThatExceptionOfType(BeanDefinitionParsingException.class) | ||||
| 				.isThrownBy(() -> this.spring.configLocations(this.xml("XssProtectionDisabledAndEnabled")).autowire()) | ||||
| 				.withMessageContaining("enabled"); | ||||
| 	} | ||||
| 
 | ||||
| 	@Test | ||||
| 	public void configureWhenXssProtectionDisabledAndBlockSpecifiedThenAutowireFails() { | ||||
| 		assertThatExceptionOfType(BeanDefinitionParsingException.class) | ||||
| 				.isThrownBy( | ||||
| 						() -> this.spring.configLocations(this.xml("XssProtectionDisabledSpecifyingBlock")).autowire()) | ||||
| 				.withMessageContaining("block"); | ||||
| 	} | ||||
| 
 | ||||
| 	@Test | ||||
| 	public void configureWhenXssProtectionDisabledAndHeaderValueSpecifiedThenAutowireFails() { | ||||
| 		assertThatExceptionOfType(BeanDefinitionParsingException.class).isThrownBy( | ||||
|  | ||||
| @ -75,7 +75,7 @@ public class HeaderSpecTests { | ||||
| 		this.expectedHeaders.add(HttpHeaders.EXPIRES, "0"); | ||||
| 		this.expectedHeaders.add(ContentTypeOptionsServerHttpHeadersWriter.X_CONTENT_OPTIONS, "nosniff"); | ||||
| 		this.expectedHeaders.add(XFrameOptionsServerHttpHeadersWriter.X_FRAME_OPTIONS, "DENY"); | ||||
| 		this.expectedHeaders.add(XXssProtectionServerHttpHeadersWriter.X_XSS_PROTECTION, "1 ; mode=block"); | ||||
| 		this.expectedHeaders.add(XXssProtectionServerHttpHeadersWriter.X_XSS_PROTECTION, "0"); | ||||
| 	} | ||||
| 
 | ||||
| 	@Test | ||||
|  | ||||
| @ -62,7 +62,7 @@ class HeadersDslTests { | ||||
|             header { string(HttpHeaders.CACHE_CONTROL, "no-cache, no-store, max-age=0, must-revalidate") } | ||||
|             header { string(HttpHeaders.EXPIRES, "0") } | ||||
|             header { string(HttpHeaders.PRAGMA, "no-cache") } | ||||
|             header { string(XXssProtectionServerHttpHeadersWriter.X_XSS_PROTECTION, "1; mode=block") } | ||||
|             header { string(XXssProtectionServerHttpHeadersWriter.X_XSS_PROTECTION, "0") } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|  | ||||
| @ -106,7 +106,7 @@ class HttpSecurityDslTests { | ||||
|                 string(HttpHeaders.PRAGMA, "no-cache") | ||||
|             } | ||||
|             header { | ||||
|                 string(XXssProtectionServerHttpHeadersWriter.X_XSS_PROTECTION, "1; mode=block") | ||||
|                 string(XXssProtectionServerHttpHeadersWriter.X_XSS_PROTECTION, "0") | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
| @ -36,6 +36,7 @@ import org.springframework.test.web.servlet.get | ||||
|  * Tests for [XssProtectionConfigDsl] | ||||
|  * | ||||
|  * @author Eleftheria Stein | ||||
|  * @author Daniel Garnier-Moiroux | ||||
|  */ | ||||
| @ExtendWith(SpringTestContextExtension::class) | ||||
| class XssProtectionConfigDslTests { | ||||
| @ -52,7 +53,7 @@ class XssProtectionConfigDslTests { | ||||
|         this.mockMvc.get("/") { | ||||
|             secure = true | ||||
|         }.andExpect { | ||||
|             header { string(XXssProtectionServerHttpHeadersWriter.X_XSS_PROTECTION, "1; mode=block") } | ||||
|             header { string(XXssProtectionServerHttpHeadersWriter.X_XSS_PROTECTION, "0") } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| @ -71,62 +72,6 @@ class XssProtectionConfigDslTests { | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     @Test | ||||
|     fun `headers when XSS protection with block false then mode is not block in header`() { | ||||
|         this.spring.register(XssProtectionBlockFalseConfig::class.java).autowire() | ||||
| 
 | ||||
|         this.mockMvc.get("/") { | ||||
|             secure = true | ||||
|         }.andExpect { | ||||
|             header { string(XXssProtectionServerHttpHeadersWriter.X_XSS_PROTECTION, "1") } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     @Configuration | ||||
|     @EnableWebSecurity | ||||
|     open class XssProtectionBlockFalseConfig { | ||||
|         @Bean | ||||
|         open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain { | ||||
|             http { | ||||
|                 headers { | ||||
|                     defaultsDisabled = true | ||||
|                     xssProtection { | ||||
|                         block = false | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|             return http.build() | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     @Test | ||||
|     fun `headers when XSS protection disabled then X-XSS-Protection header is 0`() { | ||||
|         this.spring.register(XssProtectionDisabledConfig::class.java).autowire() | ||||
| 
 | ||||
|         this.mockMvc.get("/") { | ||||
|             secure = true | ||||
|         }.andExpect { | ||||
|             header { string(XXssProtectionServerHttpHeadersWriter.X_XSS_PROTECTION, "0") } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     @Configuration | ||||
|     @EnableWebSecurity | ||||
|     open class XssProtectionDisabledConfig { | ||||
|         @Bean | ||||
|         open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain { | ||||
|             http { | ||||
|                 headers { | ||||
|                     defaultsDisabled = true | ||||
|                     xssProtection { | ||||
|                         xssProtectionEnabled = false | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|             return http.build() | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     @Test | ||||
|     fun `headers when XSS protection disabled then X-XSS-Protection header not in response`() { | ||||
|         this.spring.register(XssProtectionDisabledFunctionConfig::class.java).autowire() | ||||
| @ -155,26 +100,54 @@ class XssProtectionConfigDslTests { | ||||
|     } | ||||
| 
 | ||||
|     @Test | ||||
|     fun `headers when XSS protection header value disabled then X-XSS-Protection header is 0`() { | ||||
|         this.spring.register(XssProtectionHeaderValueDisabledFunctionConfig::class.java).autowire() | ||||
|     fun `headers when XSS protection header value enabled then X-XSS-Protection header is 1`() { | ||||
|         this.spring.register(XssProtectionHeaderValueEnabledFunctionConfig::class.java).autowire() | ||||
| 
 | ||||
|         this.mockMvc.get("/") { | ||||
|             secure = true | ||||
|         }.andExpect { | ||||
|             header { string(XXssProtectionServerHttpHeadersWriter.X_XSS_PROTECTION, "0") } | ||||
|             header { string(XXssProtectionServerHttpHeadersWriter.X_XSS_PROTECTION, "1") } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     @Configuration | ||||
|     @EnableWebSecurity | ||||
|     open class XssProtectionHeaderValueDisabledFunctionConfig () { | ||||
|     open class XssProtectionHeaderValueEnabledFunctionConfig { | ||||
|         @Bean | ||||
|         open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain { | ||||
|             http { | ||||
|                 headers { | ||||
|                     defaultsDisabled = true | ||||
|                     xssProtection { | ||||
|                         headerValue = XXssProtectionHeaderWriter.HeaderValue.DISABLED | ||||
|                         headerValue = XXssProtectionHeaderWriter.HeaderValue.ENABLED | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|             return http.build() | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     @Test | ||||
|     fun `headers when XSS protection header value enabled_mode_block then X-XSS-Protection header is 1 and mode=block`() { | ||||
|         this.spring.register(XssProtectionHeaderValueEnabledModeBlockFunctionConfig::class.java).autowire() | ||||
| 
 | ||||
|         this.mockMvc.get("/") { | ||||
|             secure = true | ||||
|         }.andExpect { | ||||
|             header { string(XXssProtectionServerHttpHeadersWriter.X_XSS_PROTECTION, "1; mode=block") } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     @Configuration | ||||
|     @EnableWebSecurity | ||||
|     open class XssProtectionHeaderValueEnabledModeBlockFunctionConfig { | ||||
|         @Bean | ||||
|         open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain { | ||||
|             http { | ||||
|                 headers { | ||||
|                     defaultsDisabled = true | ||||
|                     xssProtection { | ||||
|                         headerValue = XXssProtectionHeaderWriter.HeaderValue.ENABLED_MODE_BLOCK | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|  | ||||
| @ -71,7 +71,7 @@ class ServerHeadersDslTests { | ||||
|                 .expectHeader().valueEquals(HttpHeaders.CACHE_CONTROL, "no-cache, no-store, max-age=0, must-revalidate") | ||||
|                 .expectHeader().valueEquals(HttpHeaders.EXPIRES, "0") | ||||
|                 .expectHeader().valueEquals(HttpHeaders.PRAGMA, "no-cache") | ||||
|                 .expectHeader().valueEquals(XXssProtectionServerHttpHeadersWriter.X_XSS_PROTECTION, "1 ; mode=block") | ||||
|                 .expectHeader().valueEquals(XXssProtectionServerHttpHeadersWriter.X_XSS_PROTECTION, "0") | ||||
|     } | ||||
| 
 | ||||
|     @Configuration | ||||
|  | ||||
| @ -126,7 +126,7 @@ class ServerHttpSecurityDslTests { | ||||
|                 .expectHeader().valueEquals(HttpHeaders.CACHE_CONTROL, "no-cache, no-store, max-age=0, must-revalidate") | ||||
|                 .expectHeader().valueEquals(HttpHeaders.EXPIRES, "0") | ||||
|                 .expectHeader().valueEquals(HttpHeaders.PRAGMA, "no-cache") | ||||
|                 .expectHeader().valueEquals(XXssProtectionServerHttpHeadersWriter.X_XSS_PROTECTION, "1 ; mode=block") | ||||
|                 .expectHeader().valueEquals(XXssProtectionServerHttpHeadersWriter.X_XSS_PROTECTION, "0") | ||||
|     } | ||||
| 
 | ||||
|     @Configuration | ||||
|  | ||||
| @ -57,7 +57,7 @@ class ServerXssProtectionDslTests { | ||||
|         this.client.get() | ||||
|                 .uri("/") | ||||
|                 .exchange() | ||||
|                 .expectHeader().valueEquals(XXssProtectionServerHttpHeadersWriter.X_XSS_PROTECTION, "1 ; mode=block") | ||||
|                 .expectHeader().valueEquals(XXssProtectionServerHttpHeadersWriter.X_XSS_PROTECTION, "0") | ||||
|     } | ||||
| 
 | ||||
|     @Configuration | ||||
| @ -107,7 +107,7 @@ class ServerXssProtectionDslTests { | ||||
|         this.client.get() | ||||
|                 .uri("/") | ||||
|                 .exchange() | ||||
|                 .expectHeader().valueEquals(XXssProtectionServerHttpHeadersWriter.X_XSS_PROTECTION, "0") | ||||
|                 .expectHeader().valueEquals(XXssProtectionServerHttpHeadersWriter.X_XSS_PROTECTION, "1") | ||||
|     } | ||||
| 
 | ||||
|     @EnableWebFluxSecurity | ||||
| @ -118,7 +118,7 @@ class ServerXssProtectionDslTests { | ||||
|             return http { | ||||
|                 headers { | ||||
|                     xssProtection { | ||||
|                         headerValue = XXssProtectionServerHttpHeadersWriter.HeaderValue.DISABLED | ||||
|                         headerValue = XXssProtectionServerHttpHeadersWriter.HeaderValue.ENABLED | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|  | ||||
| @ -1,37 +0,0 @@ | ||||
| <?xml version="1.0" encoding="UTF-8"?> | ||||
| <!-- | ||||
|   ~ Copyright 2002-2018 the original author or authors. | ||||
|   ~ | ||||
|   ~ Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|   ~ you may not use this file except in compliance with the License. | ||||
|   ~ You may obtain a copy of the License at | ||||
|   ~ | ||||
|   ~      https://www.apache.org/licenses/LICENSE-2.0 | ||||
|   ~ | ||||
|   ~ Unless required by applicable law or agreed to in writing, software | ||||
|   ~ distributed under the License is distributed on an "AS IS" BASIS, | ||||
|   ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
|   ~ See the License for the specific language governing permissions and | ||||
|   ~ limitations under the License. | ||||
|   --> | ||||
| 
 | ||||
| <b:beans xmlns:b="http://www.springframework.org/schema/beans" | ||||
| 		xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | ||||
| 		xmlns="http://www.springframework.org/schema/security" | ||||
| 		xsi:schemaLocation=" | ||||
| 			http://www.springframework.org/schema/security | ||||
| 			https://www.springframework.org/schema/security/spring-security.xsd | ||||
| 			http://www.springframework.org/schema/beans | ||||
| 			https://www.springframework.org/schema/beans/spring-beans.xsd"> | ||||
| 
 | ||||
| 	<http auto-config="true"> | ||||
| 		<headers defaults-disabled="true"> | ||||
| 			<xss-protection enabled="false"/> | ||||
| 		</headers> | ||||
| 		<intercept-url pattern="/**" access="permitAll"/> | ||||
| 	</http> | ||||
| 
 | ||||
| 	<b:bean name="simple" class="org.springframework.security.config.http.HttpHeadersConfigTests.SimpleController"/> | ||||
| 
 | ||||
| 	<b:import resource="userservice.xml"/> | ||||
| </b:beans> | ||||
| @ -1,37 +0,0 @@ | ||||
| <?xml version="1.0" encoding="UTF-8"?> | ||||
| <!-- | ||||
|   ~ Copyright 2002-2018 the original author or authors. | ||||
|   ~ | ||||
|   ~ Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|   ~ you may not use this file except in compliance with the License. | ||||
|   ~ You may obtain a copy of the License at | ||||
|   ~ | ||||
|   ~      https://www.apache.org/licenses/LICENSE-2.0 | ||||
|   ~ | ||||
|   ~ Unless required by applicable law or agreed to in writing, software | ||||
|   ~ distributed under the License is distributed on an "AS IS" BASIS, | ||||
|   ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
|   ~ See the License for the specific language governing permissions and | ||||
|   ~ limitations under the License. | ||||
|   --> | ||||
| 
 | ||||
| <b:beans xmlns:b="http://www.springframework.org/schema/beans" | ||||
| 		xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | ||||
| 		xmlns="http://www.springframework.org/schema/security" | ||||
| 		xsi:schemaLocation=" | ||||
| 			http://www.springframework.org/schema/security | ||||
| 			https://www.springframework.org/schema/security/spring-security.xsd | ||||
| 			http://www.springframework.org/schema/beans | ||||
| 			https://www.springframework.org/schema/beans/spring-beans.xsd"> | ||||
| 
 | ||||
| 	<http auto-config="true"> | ||||
| 		<headers defaults-disabled="true"> | ||||
| 			<xss-protection enabled="false" block="true"/> | ||||
| 		</headers> | ||||
| 		<intercept-url pattern="/**" access="permitAll"/> | ||||
| 	</http> | ||||
| 
 | ||||
| 	<b:bean name="simple" class="org.springframework.security.config.http.HttpHeadersConfigTests.SimpleController"/> | ||||
| 
 | ||||
| 	<b:import resource="userservice.xml"/> | ||||
| </b:beans> | ||||
| @ -1,37 +0,0 @@ | ||||
| <?xml version="1.0" encoding="UTF-8"?> | ||||
| <!-- | ||||
|   ~ Copyright 2002-2022 the original author or authors. | ||||
|   ~ | ||||
|   ~ Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|   ~ you may not use this file except in compliance with the License. | ||||
|   ~ You may obtain a copy of the License at | ||||
|   ~ | ||||
|   ~      https://www.apache.org/licenses/LICENSE-2.0 | ||||
|   ~ | ||||
|   ~ Unless required by applicable law or agreed to in writing, software | ||||
|   ~ distributed under the License is distributed on an "AS IS" BASIS, | ||||
|   ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
|   ~ See the License for the specific language governing permissions and | ||||
|   ~ limitations under the License. | ||||
|   --> | ||||
| 
 | ||||
| <b:beans xmlns:b="http://www.springframework.org/schema/beans" | ||||
| 		xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | ||||
| 		xmlns="http://www.springframework.org/schema/security" | ||||
| 		xsi:schemaLocation=" | ||||
| 			http://www.springframework.org/schema/security | ||||
| 			https://www.springframework.org/schema/security/spring-security.xsd | ||||
| 			http://www.springframework.org/schema/beans | ||||
| 			https://www.springframework.org/schema/beans/spring-beans.xsd"> | ||||
| 
 | ||||
| 	<http auto-config="true"> | ||||
| 		<headers defaults-disabled="true"> | ||||
| 			<xss-protection enabled="false" header-value="1"/> | ||||
| 		</headers> | ||||
| 		<intercept-url pattern="/**" access="permitAll"/> | ||||
| 	</http> | ||||
| 
 | ||||
| 	<b:bean name="simple" class="org.springframework.security.config.http.HttpHeadersConfigTests.SimpleController"/> | ||||
| 
 | ||||
| 	<b:import resource="userservice.xml"/> | ||||
| </b:beans> | ||||
| @ -1,37 +0,0 @@ | ||||
| <?xml version="1.0" encoding="UTF-8"?> | ||||
| <!-- | ||||
|   ~ Copyright 2002-2018 the original author or authors. | ||||
|   ~ | ||||
|   ~ Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|   ~ you may not use this file except in compliance with the License. | ||||
|   ~ You may obtain a copy of the License at | ||||
|   ~ | ||||
|   ~      https://www.apache.org/licenses/LICENSE-2.0 | ||||
|   ~ | ||||
|   ~ Unless required by applicable law or agreed to in writing, software | ||||
|   ~ distributed under the License is distributed on an "AS IS" BASIS, | ||||
|   ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
|   ~ See the License for the specific language governing permissions and | ||||
|   ~ limitations under the License. | ||||
|   --> | ||||
| 
 | ||||
| <b:beans xmlns:b="http://www.springframework.org/schema/beans" | ||||
| 		xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | ||||
| 		xmlns="http://www.springframework.org/schema/security" | ||||
| 		xsi:schemaLocation=" | ||||
| 			http://www.springframework.org/schema/security | ||||
| 			https://www.springframework.org/schema/security/spring-security.xsd | ||||
| 			http://www.springframework.org/schema/beans | ||||
| 			https://www.springframework.org/schema/beans/spring-beans.xsd"> | ||||
| 
 | ||||
| 	<http auto-config="true"> | ||||
| 		<headers defaults-disabled="true"> | ||||
| 			<xss-protection enabled="true"/> | ||||
| 		</headers> | ||||
| 		<intercept-url pattern="/**" access="permitAll"/> | ||||
| 	</http> | ||||
| 
 | ||||
| 	<b:bean name="simple" class="org.springframework.security.config.http.HttpHeadersConfigTests.SimpleController"/> | ||||
| 
 | ||||
| 	<b:import resource="userservice.xml"/> | ||||
| </b:beans> | ||||
| @ -1,37 +0,0 @@ | ||||
| <?xml version="1.0" encoding="UTF-8"?> | ||||
| <!-- | ||||
|   ~ Copyright 2002-2018 the original author or authors. | ||||
|   ~ | ||||
|   ~ Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|   ~ you may not use this file except in compliance with the License. | ||||
|   ~ You may obtain a copy of the License at | ||||
|   ~ | ||||
|   ~      https://www.apache.org/licenses/LICENSE-2.0 | ||||
|   ~ | ||||
|   ~ Unless required by applicable law or agreed to in writing, software | ||||
|   ~ distributed under the License is distributed on an "AS IS" BASIS, | ||||
|   ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
|   ~ See the License for the specific language governing permissions and | ||||
|   ~ limitations under the License. | ||||
|   --> | ||||
| 
 | ||||
| <b:beans xmlns:b="http://www.springframework.org/schema/beans" | ||||
| 		xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | ||||
| 		xmlns="http://www.springframework.org/schema/security" | ||||
| 		xsi:schemaLocation=" | ||||
| 			http://www.springframework.org/schema/security | ||||
| 			https://www.springframework.org/schema/security/spring-security.xsd | ||||
| 			http://www.springframework.org/schema/beans | ||||
| 			https://www.springframework.org/schema/beans/spring-beans.xsd"> | ||||
| 
 | ||||
| 	<http auto-config="true"> | ||||
| 		<headers> | ||||
| 			<xss-protection disabled="true" enabled="true"/> | ||||
| 		</headers> | ||||
| 		<intercept-url pattern="/**" access="permitAll"/> | ||||
| 	</http> | ||||
| 
 | ||||
| 	<b:bean name="simple" class="org.springframework.security.config.http.HttpHeadersConfigTests.SimpleController"/> | ||||
| 
 | ||||
| 	<b:import resource="userservice.xml"/> | ||||
| </b:beans> | ||||
| @ -1,37 +0,0 @@ | ||||
| <?xml version="1.0" encoding="UTF-8"?> | ||||
| <!-- | ||||
|   ~ Copyright 2002-2018 the original author or authors. | ||||
|   ~ | ||||
|   ~ Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|   ~ you may not use this file except in compliance with the License. | ||||
|   ~ You may obtain a copy of the License at | ||||
|   ~ | ||||
|   ~      https://www.apache.org/licenses/LICENSE-2.0 | ||||
|   ~ | ||||
|   ~ Unless required by applicable law or agreed to in writing, software | ||||
|   ~ distributed under the License is distributed on an "AS IS" BASIS, | ||||
|   ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
|   ~ See the License for the specific language governing permissions and | ||||
|   ~ limitations under the License. | ||||
|   --> | ||||
| 
 | ||||
| <b:beans xmlns:b="http://www.springframework.org/schema/beans" | ||||
| 		xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | ||||
| 		xmlns="http://www.springframework.org/schema/security" | ||||
| 		xsi:schemaLocation=" | ||||
| 			http://www.springframework.org/schema/security | ||||
| 			https://www.springframework.org/schema/security/spring-security.xsd | ||||
| 			http://www.springframework.org/schema/beans | ||||
| 			https://www.springframework.org/schema/beans/spring-beans.xsd"> | ||||
| 
 | ||||
| 	<http auto-config="true"> | ||||
| 		<headers> | ||||
| 			<xss-protection disabled="true" block="true"/> | ||||
| 		</headers> | ||||
| 		<intercept-url pattern="/**" access="permitAll"/> | ||||
| 	</http> | ||||
| 
 | ||||
| 	<b:bean name="simple" class="org.springframework.security.config.http.HttpHeadersConfigTests.SimpleController"/> | ||||
| 
 | ||||
| 	<b:import resource="userservice.xml"/> | ||||
| </b:beans> | ||||
| @ -33,7 +33,7 @@ Expires: 0 | ||||
| X-Content-Type-Options: nosniff | ||||
| Strict-Transport-Security: max-age=31536000 ; includeSubDomains | ||||
| X-Frame-Options: DENY | ||||
| X-XSS-Protection: 1; mode=block | ||||
| X-XSS-Protection: 0 | ||||
| ---- | ||||
| ==== | ||||
| 
 | ||||
| @ -209,18 +209,14 @@ See the relevant sections to see how to customize the defaults for both xref:ser | ||||
| ==== | ||||
| 
 | ||||
| Some browsers have built-in support for filtering out https://www.owasp.org/index.php/Testing_for_Reflected_Cross_site_scripting_(OWASP-DV-001)[reflected XSS attacks]. | ||||
| This is by no means foolproof but does assist in XSS protection. | ||||
| The filter has been deprecated in major browsers, and https://cheatsheetseries.owasp.org/cheatsheets/HTTP_Headers_Cheat_Sheet.html#x-xss-protection[current OWASP recommendation] is to explicitly set the header to 0. | ||||
| 
 | ||||
| The filtering is typically enabled by default, so adding the header typically just ensures it is enabled and instructs the browser what to do when a XSS attack is detected. | ||||
| For example, the filter might try to change the content in the least invasive way to still render everything. | ||||
| At times, this type of replacement can become an https://hackademix.net/2009/11/21/ies-xss-filter-creates-xss-vulnerabilities/[XSS vulnerability in itself]. | ||||
| Instead, it is best to block the content rather than attempt to fix it. | ||||
| By default, Spring Security blocks the content by using the following header: | ||||
| 
 | ||||
| ==== | ||||
| [source] | ||||
| ---- | ||||
| X-XSS-Protection: 1; mode=block | ||||
| X-XSS-Protection: 0 | ||||
| ---- | ||||
| ==== | ||||
| 
 | ||||
|  | ||||
| @ -255,8 +255,8 @@ fun webFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain { | ||||
| 
 | ||||
| [[webflux-headers-xss-protection]] | ||||
| == X-XSS-Protection | ||||
| By default, Spring Security instructs browsers to block reflected XSS attacks by using the <<headers-xss-protection,X-XSS-Protection header>. | ||||
| You can disable `X-XSS-Protection`: | ||||
| By default, Spring Security instructs browsers to disable the XSS Auditor by using <<headers-xss-protection,X-XSS-Protection header>. | ||||
| You can disable the `X-XSS-Protection` header entirely: | ||||
| 
 | ||||
| .X-XSS-Protection Customization | ||||
| ==== | ||||
| @ -291,6 +291,41 @@ fun webFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain { | ||||
| ---- | ||||
| ==== | ||||
| 
 | ||||
| You can also change the header value: | ||||
| 
 | ||||
| .X-XSS-Protection Explicit header value | ||||
| ==== | ||||
| .Java | ||||
| [source,java,role="primary"] | ||||
| ---- | ||||
| @Bean | ||||
| SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) { | ||||
| 	http | ||||
| 		// ... | ||||
| 		.headers(headers -> headers | ||||
| 			.xssProtection(xssProtection -> xssProtection.headerValue(XXssProtectionServerHttpHeadersWriter.HeaderValue.ENABLED_MODE_BLOCK)) | ||||
| 		); | ||||
| 	return http.build(); | ||||
| } | ||||
| ---- | ||||
| 
 | ||||
| .Kotlin | ||||
| [source,kotlin,role="secondary"] | ||||
| ---- | ||||
| @Bean | ||||
| fun webFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain { | ||||
|     return http { | ||||
|         // ... | ||||
|         headers { | ||||
|             xssProtection { | ||||
|                 headerValue = XXssProtectionServerHttpHeadersWriter.HeaderValue.ENABLED_MODE_BLOCK | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| ---- | ||||
| ==== | ||||
| 
 | ||||
| [[webflux-headers-csp]] | ||||
| == Content Security Policy (CSP) | ||||
| By default, Spring Security does not add xref:features/exploits/headers.adoc#headers-csp[Content Security Policy], because a reasonable default is impossible to know without the context of the application. | ||||
|  | ||||
| @ -571,23 +571,10 @@ This is in no-way a full protection to XSS attacks! | ||||
| Do not include the header for https://en.wikipedia.org/wiki/Cross-site_scripting#Non-Persistent[reflected / Type-1 Cross-Site Scripting (XSS)] protection. | ||||
| 
 | ||||
| 
 | ||||
| [[nsa-xss-protection-enabled]] | ||||
| * **xss-protection-enabled** | ||||
| Explicitly enable or disable https://en.wikipedia.org/wiki/Cross-site_scripting#Non-Persistent[reflected / Type-1 Cross-Site Scripting (XSS)] protection. | ||||
| 
 | ||||
| 
 | ||||
| [[nsa-xss-protection-block]] | ||||
| * **xss-protection-block** | ||||
| When true and xss-protection-enabled is true, adds mode=block to the header. | ||||
| This indicates to the browser that the page should not be loaded at all. | ||||
| When false and xss-protection-enabled is true, the page will still be rendered when an reflected attack is detected but the response will be modified to protect against the attack. | ||||
| Note that there are sometimes ways of bypassing this mode which can often times make blocking the page more desirable. | ||||
| 
 | ||||
| [[nsa-xss-protection-header-value]] | ||||
| * **xss-protection-header-value** | ||||
| Explicitly set the value for https://en.wikipedia.org/wiki/Cross-site_scripting#Non-Persistent[reflected / Type-1 Cross-Site Scripting (XSS)] header. | ||||
| One of: "0", "1", "1; mode=block". | ||||
| When set, overrides both enabled and block attributes. | ||||
| One of: "0", "1", "1; mode=block". Defaults to "0". | ||||
| 
 | ||||
| 
 | ||||
| [[nsa-xss-protection-parents]] | ||||
|  | ||||
| @ -529,9 +529,10 @@ class SecurityConfig { | ||||
| [[servlet-headers-xss-protection]] | ||||
| == X-XSS-Protection | ||||
| 
 | ||||
| By default, Spring Security instructs browsers to block reflected XSS attacks by using the <<headers-xss-protection,X-XSS-Protection header>. | ||||
| By default, Spring Security instructs browsers to disable the XSS Auditor by using <<headers-xss-protection,X-XSS-Protection header>. | ||||
| However, you can change this default. | ||||
| For example, the following configuration specifies that Spring Security should no longer instruct browsers to block the content: | ||||
| For example, the following configuration specifies that Spring Security instruct compatible browsers to enable filtering, | ||||
| and block the content: | ||||
| 
 | ||||
| .X-XSS-Protection Customization | ||||
| ==== | ||||
| @ -548,7 +549,7 @@ public class WebSecurityConfig { | ||||
| 			// ... | ||||
| 			.headers(headers -> headers | ||||
| 				.xssProtection(xss -> xss | ||||
| 					.block(false) | ||||
| 					.headerValue(XXssProtectionHeaderWriter.HeaderValue.ENABLED_MODE_BLOCK) | ||||
| 				) | ||||
| 			); | ||||
| 		return http.build(); | ||||
| @ -563,7 +564,7 @@ public class WebSecurityConfig { | ||||
| 	<!-- ... --> | ||||
| 
 | ||||
| 	<headers> | ||||
| 		<xss-protection block="false"/> | ||||
| 		<xss-protection headerValue="1; mode=block"/> | ||||
| 	</headers> | ||||
| </http> | ||||
| ---- | ||||
| @ -581,7 +582,7 @@ class SecurityConfig { | ||||
|         http { | ||||
|             headers { | ||||
|                 xssProtection { | ||||
|                     block = false | ||||
|                     headerValue = XXssProtectionHeaderWriter.HeaderValue.ENABLED_MODE_BLOCK | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
| @ -29,6 +29,7 @@ import org.springframework.util.Assert; | ||||
|  * | ||||
|  * @author Rob Winch | ||||
|  * @author Ankur Pathak | ||||
|  * @author Daniel Garnier-Moiroux | ||||
|  * @since 3.2 | ||||
|  */ | ||||
| public final class XXssProtectionHeaderWriter implements HeaderWriter { | ||||
| @ -41,7 +42,7 @@ public final class XXssProtectionHeaderWriter implements HeaderWriter { | ||||
| 	 * Create a new instance | ||||
| 	 */ | ||||
| 	public XXssProtectionHeaderWriter() { | ||||
| 		this.headerValue = HeaderValue.ENABLED_MODE_BLOCK; | ||||
| 		this.headerValue = HeaderValue.DISABLED; | ||||
| 	} | ||||
| 
 | ||||
| 	@Override | ||||
| @ -51,55 +52,6 @@ public final class XXssProtectionHeaderWriter implements HeaderWriter { | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	/** | ||||
| 	 * If true, will contain a value of 1. For example: | ||||
| 	 * | ||||
| 	 * <pre> | ||||
| 	 * X-XSS-Protection: 1 | ||||
| 	 * </pre> | ||||
| 	 * | ||||
| 	 * or if {@link #setBlock(boolean)} is true | ||||
| 	 * | ||||
| 	 * | ||||
| 	 * <pre> | ||||
| 	 * X-XSS-Protection: 1; mode=block | ||||
| 	 * </pre> | ||||
| 	 * | ||||
| 	 * If false, will explicitly disable specify that X-XSS-Protection is disabled. For | ||||
| 	 * example: | ||||
| 	 * | ||||
| 	 * <pre> | ||||
| 	 * X-XSS-Protection: 0 | ||||
| 	 * </pre> | ||||
| 	 * @param enabled the new value | ||||
| 	 * @deprecated use {@link XXssProtectionHeaderWriter#setHeaderValue(HeaderValue)} | ||||
| 	 * instead | ||||
| 	 */ | ||||
| 	@Deprecated | ||||
| 	public void setEnabled(boolean enabled) { | ||||
| 		if (!enabled) { | ||||
| 			this.headerValue = HeaderValue.DISABLED; | ||||
| 		} | ||||
| 		else if (this.headerValue == HeaderValue.DISABLED) { | ||||
| 			this.headerValue = HeaderValue.ENABLED; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	/** | ||||
| 	 * If false, will not specify the mode as blocked. In this instance, any content will | ||||
| 	 * be attempted to be fixed. If true, the content will be replaced with "#". | ||||
| 	 * @param block the new value | ||||
| 	 * @deprecated use {@link XXssProtectionHeaderWriter#setHeaderValue(HeaderValue)} | ||||
| 	 * instead | ||||
| 	 */ | ||||
| 	@Deprecated | ||||
| 	public void setBlock(boolean block) { | ||||
| 		if (this.headerValue == HeaderValue.DISABLED && block) { | ||||
| 			throw new IllegalArgumentException("Cannot set block to true with enabled false"); | ||||
| 		} | ||||
| 		this.headerValue = block ? HeaderValue.ENABLED_MODE_BLOCK : HeaderValue.ENABLED; | ||||
| 	} | ||||
| 
 | ||||
| 	/** | ||||
| 	 * Sets the value of the X-XSS-PROTECTION header. | ||||
| 	 * <p> | ||||
|  | ||||
| @ -41,7 +41,7 @@ public class XXssProtectionServerHttpHeadersWriter implements ServerHttpHeadersW | ||||
| 	 * Creates a new instance | ||||
| 	 */ | ||||
| 	public XXssProtectionServerHttpHeadersWriter() { | ||||
| 		this.headerValue = HeaderValue.ENABLED_MODE_BLOCK; | ||||
| 		this.headerValue = HeaderValue.DISABLED; | ||||
| 		updateDelegate(); | ||||
| 	} | ||||
| 
 | ||||
| @ -51,57 +51,8 @@ public class XXssProtectionServerHttpHeadersWriter implements ServerHttpHeadersW | ||||
| 	} | ||||
| 
 | ||||
| 	/** | ||||
| 	 * If true, will contain a value of 1. For example: | ||||
| 	 * | ||||
| 	 * <pre> | ||||
| 	 * X-XSS-Protection: 1 | ||||
| 	 * </pre> | ||||
| 	 * | ||||
| 	 * or if {@link #setBlock(boolean)} is true | ||||
| 	 * | ||||
| 	 * | ||||
| 	 * <pre> | ||||
| 	 * X-XSS-Protection: 1; mode=block | ||||
| 	 * </pre> | ||||
| 	 * | ||||
| 	 * If false, will explicitly disable specify that X-XSS-Protection is disabled. For | ||||
| 	 * example: | ||||
| 	 * | ||||
| 	 * <pre> | ||||
| 	 * X-XSS-Protection: 0 | ||||
| 	 * </pre> | ||||
| 	 * @param enabled the new value | ||||
| 	 * @deprecated use | ||||
| 	 * {@link XXssProtectionServerHttpHeadersWriter#setHeaderValue(HeaderValue)} instead | ||||
| 	 */ | ||||
| 	@Deprecated | ||||
| 	public void setEnabled(boolean enabled) { | ||||
| 		if (!enabled) { | ||||
| 			this.headerValue = HeaderValue.DISABLED; | ||||
| 		} | ||||
| 		else if (this.headerValue == HeaderValue.DISABLED) { | ||||
| 			this.headerValue = HeaderValue.ENABLED; | ||||
| 		} | ||||
| 		updateDelegate(); | ||||
| 	} | ||||
| 
 | ||||
| 	/** | ||||
| 	 * If false, will not specify the mode as blocked. In this instance, any content will | ||||
| 	 * be attempted to be fixed. If true, the content will be replaced with "#". | ||||
| 	 * @param block the new value | ||||
| 	 * @deprecated use | ||||
| 	 * {@link XXssProtectionServerHttpHeadersWriter#setHeaderValue(HeaderValue)} instead | ||||
| 	 */ | ||||
| 	@Deprecated | ||||
| 	public void setBlock(boolean block) { | ||||
| 		Assert.isTrue(this.headerValue != HeaderValue.DISABLED || !block, | ||||
| 				"Cannot set block to true with enabled false"); | ||||
| 		this.headerValue = block ? HeaderValue.ENABLED_MODE_BLOCK : HeaderValue.ENABLED; | ||||
| 		updateDelegate(); | ||||
| 	} | ||||
| 
 | ||||
| 	/** | ||||
| 	 * Sets the value of the X-XSS-PROTECTION header. | ||||
| 	 * Sets the value of the X-XSS-PROTECTION header. Defaults to | ||||
| 	 * {@link HeaderValue#DISABLED} | ||||
| 	 * <p> | ||||
| 	 * If {@link HeaderValue#DISABLED}, will specify that X-XSS-Protection is disabled. | ||||
| 	 * For example: | ||||
|  | ||||
| @ -28,6 +28,7 @@ import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException | ||||
| /** | ||||
|  * @author Rob Winch | ||||
|  * @author Ankur Pathak | ||||
|  * @author Daniel Garnier-Moiroux | ||||
|  * | ||||
|  */ | ||||
| public class XXssProtectionHeaderWriterTests { | ||||
| @ -49,43 +50,11 @@ public class XXssProtectionHeaderWriterTests { | ||||
| 
 | ||||
| 	@Test | ||||
| 	public void writeHeaders() { | ||||
| 		this.writer.writeHeaders(this.request, this.response); | ||||
| 		assertThat(this.response.getHeaderNames()).hasSize(1); | ||||
| 		assertThat(this.response.getHeaderValues("X-XSS-Protection")).containsOnly("1; mode=block"); | ||||
| 	} | ||||
| 
 | ||||
| 	@Test | ||||
| 	public void writeHeadersNoBlock() { | ||||
| 		this.writer.setBlock(false); | ||||
| 		this.writer.writeHeaders(this.request, this.response); | ||||
| 		assertThat(this.response.getHeaderNames()).hasSize(1); | ||||
| 		assertThat(this.response.getHeaderValues("X-XSS-Protection")).containsOnly("1"); | ||||
| 	} | ||||
| 
 | ||||
| 	@Test | ||||
| 	public void writeHeadersDisabled() { | ||||
| 		this.writer.setBlock(false); | ||||
| 		this.writer.setEnabled(false); | ||||
| 		this.writer.writeHeaders(this.request, this.response); | ||||
| 		assertThat(this.response.getHeaderNames()).hasSize(1); | ||||
| 		assertThat(this.response.getHeaderValues("X-XSS-Protection")).containsOnly("0"); | ||||
| 	} | ||||
| 
 | ||||
| 	@Test | ||||
| 	public void setEnabledFalseWithBlockTrue() { | ||||
| 		this.writer.setEnabled(false); | ||||
| 		this.writer.writeHeaders(this.request, this.response); | ||||
| 		assertThat(this.response.getHeaderNames()).hasSize(1); | ||||
| 		assertThat(this.response.getHeaderValues("X-XSS-Protection")).containsOnly("0"); | ||||
| 	} | ||||
| 
 | ||||
| 	@Test | ||||
| 	public void setBlockTrueWithEnabledFalse() { | ||||
| 		this.writer.setBlock(false); | ||||
| 		this.writer.setEnabled(false); | ||||
| 		assertThatIllegalArgumentException().isThrownBy(() -> this.writer.setBlock(true)); | ||||
| 	} | ||||
| 
 | ||||
| 	@Test | ||||
| 	public void writeHeaderWhenNotPresent() { | ||||
| 		String value = new String("value"); | ||||
|  | ||||
| @ -46,23 +46,6 @@ public class XXssProtectionServerHttpHeadersWriterTests { | ||||
| 
 | ||||
| 	@Test | ||||
| 	public void writeHeadersWhenNoHeadersThenWriteHeaders() { | ||||
| 		this.writer.writeHttpHeaders(this.exchange); | ||||
| 		assertThat(this.headers).hasSize(1); | ||||
| 		assertThat(this.headers.get(XXssProtectionServerHttpHeadersWriter.X_XSS_PROTECTION)) | ||||
| 				.containsOnly("1 ; mode=block"); | ||||
| 	} | ||||
| 
 | ||||
| 	@Test | ||||
| 	public void writeHeadersWhenBlockFalseThenWriteHeaders() { | ||||
| 		this.writer.setBlock(false); | ||||
| 		this.writer.writeHttpHeaders(this.exchange); | ||||
| 		assertThat(this.headers).hasSize(1); | ||||
| 		assertThat(this.headers.get(XXssProtectionServerHttpHeadersWriter.X_XSS_PROTECTION)).containsOnly("1"); | ||||
| 	} | ||||
| 
 | ||||
| 	@Test | ||||
| 	public void writeHeadersWhenEnabledFalseThenWriteHeaders() { | ||||
| 		this.writer.setEnabled(false); | ||||
| 		this.writer.writeHttpHeaders(this.exchange); | ||||
| 		assertThat(this.headers).hasSize(1); | ||||
| 		assertThat(this.headers.get(XXssProtectionServerHttpHeadersWriter.X_XSS_PROTECTION)).containsOnly("0"); | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user