SEC-2098, SEC-2099: Created HeadersFilter
Created HeadersFilter for setting security headers added including a bean definition parser for easy configuration of the headers. Enables easy configuration for the X-Frame-Options, X-XSS-Protection and X-Content-Type-Options headers. Also allows for additional headers to be added.
This commit is contained in:
parent
f5a30e55a3
commit
0adf5aea91
|
@ -54,4 +54,5 @@ public abstract class Elements {
|
|||
public static final String LDAP_PASSWORD_COMPARE = "password-compare";
|
||||
public static final String DEBUG = "debug";
|
||||
public static final String HTTP_FIREWALL = "http-firewall";
|
||||
public static final String ADD_HEADERS = "add-headers";
|
||||
}
|
||||
|
|
|
@ -0,0 +1,124 @@
|
|||
/*
|
||||
* Copyright 2002-2012 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
|
||||
*
|
||||
* http://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.
|
||||
*/
|
||||
package org.springframework.security.config.http;
|
||||
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.beans.factory.xml.BeanDefinitionParser;
|
||||
import org.springframework.beans.factory.xml.ParserContext;
|
||||
import org.springframework.security.web.headers.HeadersFilter;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.util.xml.DomUtils;
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Parser for the {@code HeadersFilter}.
|
||||
*
|
||||
* @author Marten Deinum
|
||||
* @since 3.2
|
||||
*/
|
||||
public class HeadersBeanDefinitionParser implements BeanDefinitionParser {
|
||||
|
||||
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_ORIGIN = "origin";
|
||||
|
||||
private static final String ATT_NAME = "name";
|
||||
private static final String ATT_VALUE = "value";
|
||||
|
||||
private static final String XSS_ELEMENT = "xss-protection";
|
||||
private static final String CONTENT_TYPE_ELEMENT = "content-type-options";
|
||||
private static final String FRAME_OPTIONS_ELEMENT = "frame-options";
|
||||
private static final String GENERIC_HEADER_ELEMENT = "header";
|
||||
|
||||
private static final String XSS_PROTECTION_HEADER = "X-XSS-Protection";
|
||||
private static final String FRAME_OPTIONS_HEADER = "X-Frame-Options";
|
||||
private static final String CONTENT_TYPE_OPTIONS_HEADER = "X-Content-Type-Options";
|
||||
|
||||
private static final String ALLOW_FROM = "ALLOW-FROM";
|
||||
|
||||
public BeanDefinition parse(Element element, ParserContext parserContext) {
|
||||
BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(HeadersFilter.class);
|
||||
final Map<String, String> headers = new HashMap<String, String>();
|
||||
|
||||
parseXssElement(element, headers);
|
||||
parseFrameOptionsElement(element, parserContext, headers);
|
||||
parseContentTypeOptionsElement(element, headers);
|
||||
|
||||
parseHeaderElements(element, headers);
|
||||
|
||||
builder.addPropertyValue("headers", headers);
|
||||
return builder.getBeanDefinition();
|
||||
}
|
||||
|
||||
private void parseHeaderElements(Element element, Map<String, String> headers) {
|
||||
List<Element> headerEtls = DomUtils.getChildElementsByTagName(element, GENERIC_HEADER_ELEMENT);
|
||||
for (Element headerEtl : headerEtls) {
|
||||
headers.put(headerEtl.getAttribute(ATT_NAME), headerEtl.getAttribute(ATT_VALUE));
|
||||
}
|
||||
}
|
||||
|
||||
private void parseContentTypeOptionsElement(Element element, Map<String, String> headers) {
|
||||
Element contentTypeElt = DomUtils.getChildElementByTagName(element, CONTENT_TYPE_ELEMENT);
|
||||
if (contentTypeElt != null) {
|
||||
headers.put(CONTENT_TYPE_OPTIONS_HEADER, "nosniff");
|
||||
}
|
||||
}
|
||||
|
||||
private void parseFrameOptionsElement(Element element, ParserContext parserContext, Map<String, String> headers) {
|
||||
Element frameElt = DomUtils.getChildElementByTagName(element, FRAME_OPTIONS_ELEMENT);
|
||||
if (frameElt != null) {
|
||||
String header = getAttribute(frameElt, ATT_POLICY, "DENY");
|
||||
if (ALLOW_FROM.equals(header) ) {
|
||||
String origin = frameElt.getAttribute(ATT_ORIGIN);
|
||||
if (!StringUtils.hasText(origin) ) {
|
||||
parserContext.getReaderContext().error("Frame options header value ALLOW-FROM required an origin to be specified.", frameElt);
|
||||
}
|
||||
header += " " + origin;
|
||||
}
|
||||
headers.put(FRAME_OPTIONS_HEADER, header);
|
||||
}
|
||||
}
|
||||
|
||||
private void parseXssElement(Element element, Map<String, String> headers) {
|
||||
Element xssElt = DomUtils.getChildElementByTagName(element, XSS_ELEMENT);
|
||||
if (xssElt != null) {
|
||||
boolean enabled = Boolean.valueOf(getAttribute(xssElt, ATT_ENABLED, "true"));
|
||||
boolean block = Boolean.valueOf(getAttribute(xssElt, ATT_BLOCK, "true"));
|
||||
|
||||
String value = enabled ? "1" : "0";
|
||||
if (enabled && block) {
|
||||
value += "; mode=block";
|
||||
}
|
||||
headers.put(XSS_PROTECTION_HEADER, value);
|
||||
}
|
||||
}
|
||||
|
||||
private String getAttribute(Element element, String name, String defaultValue) {
|
||||
String value = element.getAttribute(name);
|
||||
if (StringUtils.hasText(value)) {
|
||||
return value;
|
||||
} else {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -117,6 +117,7 @@ class HttpConfigurationBuilder {
|
|||
private final BeanReference portResolver;
|
||||
private BeanReference fsi;
|
||||
private BeanReference requestCache;
|
||||
private BeanDefinition addHeadersFilter;
|
||||
|
||||
public HttpConfigurationBuilder(Element element, ParserContext pc,
|
||||
BeanReference portMapper, BeanReference portResolver, BeanReference authenticationManager) {
|
||||
|
@ -151,6 +152,7 @@ class HttpConfigurationBuilder {
|
|||
createJaasApiFilter();
|
||||
createChannelProcessingFilter();
|
||||
createFilterSecurityInterceptor(authenticationManager);
|
||||
createAddHeadersFilter();
|
||||
}
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
|
@ -554,6 +556,14 @@ class HttpConfigurationBuilder {
|
|||
this.fsi = new RuntimeBeanReference(fsiId);
|
||||
}
|
||||
|
||||
private void createAddHeadersFilter() {
|
||||
Element elmt = DomUtils.getChildElementByTagName(httpElt, Elements.ADD_HEADERS);
|
||||
if (elmt != null) {
|
||||
this.addHeadersFilter = new HeadersBeanDefinitionParser().parse(elmt, pc);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
BeanReference getSessionStrategy() {
|
||||
return sessionStrategyRef;
|
||||
}
|
||||
|
@ -601,6 +611,10 @@ class HttpConfigurationBuilder {
|
|||
filters.add(new OrderDecorator(requestCacheAwareFilter, REQUEST_CACHE_FILTER));
|
||||
}
|
||||
|
||||
if (addHeadersFilter != null) {
|
||||
filters.add(new OrderDecorator(addHeadersFilter, HEADERS_FILTER));
|
||||
}
|
||||
|
||||
return filters;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,6 +29,7 @@ enum SecurityFilters {
|
|||
CONCURRENT_SESSION_FILTER,
|
||||
/** {@link WebAsyncManagerIntegrationFilter} */
|
||||
WEB_ASYNC_MANAGER_FILTER,
|
||||
HEADERS_FILTER,
|
||||
LOGOUT_FILTER,
|
||||
X509_FILTER,
|
||||
PRE_AUTH_FILTER,
|
||||
|
|
|
@ -281,7 +281,7 @@ http-firewall =
|
|||
|
||||
http =
|
||||
## Container element for HTTP security configuration. Multiple elements can now be defined, each with a specific pattern to which the enclosed security configuration applies. A pattern can also be configured to bypass Spring Security's filters completely by setting the "secured" attribute to "false".
|
||||
element http {http.attlist, (intercept-url* & access-denied-handler? & form-login? & openid-login? & x509? & jee? & http-basic? & logout? & session-management & remember-me? & anonymous? & port-mappings & custom-filter* & request-cache? & expression-handler?) }
|
||||
element http {http.attlist, (intercept-url* & access-denied-handler? & form-login? & openid-login? & x509? & jee? & http-basic? & logout? & session-management & remember-me? & anonymous? & port-mappings & custom-filter* & request-cache? & expression-handler? & headers?) }
|
||||
http.attlist &=
|
||||
## The request URL pattern which will be mapped to the filter chain created by this <http> element. If omitted, the filter chain will match all requests.
|
||||
attribute pattern {xsd:token}?
|
||||
|
@ -718,6 +718,43 @@ jdbc-user-service.attlist &=
|
|||
jdbc-user-service.attlist &=
|
||||
role-prefix?
|
||||
|
||||
headers =
|
||||
## Element for configuration of the AddHeadersFilter. Enables easy setting for the X-Frame-Options, X-XSS-Protection and X-Content-Type-Options headers.
|
||||
element headers {xss-protection? & frame-options? & content-type-options? & header*}
|
||||
|
||||
frame-options =
|
||||
## Enable basic clickjacking support for newer browsers (IE8+), will set the X-Frame-Options header.
|
||||
element frame-options {frame-options.attlist,empty}
|
||||
frame-options.attlist &=
|
||||
## Specify the policy to use for the X-Frame-Options-Header.
|
||||
attribute policy {"DENY","SAMEORIGIN","ALLOW-FROM"}?
|
||||
frame-options.attlist &=
|
||||
## Specify the origin to use when ALLOW-FROM is chosen.
|
||||
attribute origin {xsd:token}?
|
||||
|
||||
xss-protection =
|
||||
## Enable basic XSS browser protection, supported by newer browsers (IE8+), will set the X-XSS-Protection header.
|
||||
element xss-protection {xss-protection.attlist,empty}
|
||||
xss-protection.attlist &=
|
||||
## enable or disable the X-XSS-Protection header. 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}?
|
||||
|
||||
content-type-options =
|
||||
## Add a X-Content-Type-Options header to the resopnse. Value is always 'nosniff'.
|
||||
element content-type-options {empty}
|
||||
|
||||
header=
|
||||
## Add additional headers to the response.
|
||||
element header {header.attlist}
|
||||
header.attlist &=
|
||||
## The name of the header to add.
|
||||
attribute name {xsd:token}
|
||||
header.attlist &=
|
||||
## The value for the header.
|
||||
attribute value {xsd:token}
|
||||
|
||||
any-user-service = user-service | jdbc-user-service | ldap-user-service
|
||||
|
||||
|
|
|
@ -1024,6 +1024,7 @@
|
|||
<xs:attributeGroup ref="security:ref"/>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
<xs:element ref="security:headers"/>
|
||||
</xs:choice>
|
||||
<xs:attributeGroup ref="security:http.attlist"/>
|
||||
</xs:complexType>
|
||||
|
@ -2231,6 +2232,106 @@
|
|||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
</xs:attributeGroup>
|
||||
<xs:element name="headers">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Element for configuration of the AddHeadersFilter. Enables easy setting for the
|
||||
X-Frame-Options, X-XSS-Protection and X-Content-Type-Options headers.
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:complexType>
|
||||
<xs:choice minOccurs="0" maxOccurs="unbounded">
|
||||
<xs:element ref="security:xss-protection"/>
|
||||
<xs:element ref="security:frame-options"/>
|
||||
<xs:element ref="security:content-type-options"/>
|
||||
<xs:element ref="security:header"/>
|
||||
</xs:choice>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
<xs:element name="frame-options">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Enable basic clickjacking support for newer browsers (IE8+), will set the X-Frame-Options
|
||||
header.
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:complexType>
|
||||
<xs:attributeGroup ref="security:frame-options.attlist"/>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
<xs:attributeGroup name="frame-options.attlist">
|
||||
<xs:attribute name="policy">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Specify the policy to use for the X-Frame-Options-Header.
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:simpleType>
|
||||
<xs:restriction base="xs:token">
|
||||
<xs:enumeration value="DENY"/>
|
||||
<xs:enumeration value="SAMEORIGIN"/>
|
||||
<xs:enumeration value="ALLOW-FROM"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="origin" type="xs:token">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Specify the origin to use when ALLOW-FROM is chosen.
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
</xs:attributeGroup>
|
||||
<xs:element name="xss-protection">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Enable basic XSS browser protection, supported by newer browsers (IE8+), will set the
|
||||
X-XSS-Protection header.
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:complexType>
|
||||
<xs:attributeGroup ref="security:xss-protection.attlist"/>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
<xs:attributeGroup name="xss-protection.attlist">
|
||||
<xs:attribute name="enabled" type="xs:boolean">
|
||||
<xs:annotation>
|
||||
<xs:documentation>enable or disable the X-XSS-Protection header. 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:attributeGroup>
|
||||
<xs:element name="content-type-options">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Add a X-Content-Type-Options header to the resopnse. Value is always 'nosniff'.
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:complexType/>
|
||||
</xs:element>
|
||||
<xs:element name="header">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Add additional headers to the response.
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:complexType>
|
||||
<xs:attributeGroup ref="security:header.attlist"/>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
<xs:attributeGroup name="header.attlist">
|
||||
<xs:attribute name="name" use="required" type="xs:token">
|
||||
<xs:annotation>
|
||||
<xs:documentation>The name of the header to add.
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="value" use="required" type="xs:token">
|
||||
<xs:annotation>
|
||||
<xs:documentation>The value for the header.
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
</xs:attributeGroup>
|
||||
<xs:element name="any-user-service" abstract="true"/>
|
||||
<xs:element name="custom-filter">
|
||||
<xs:annotation>
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
<xsl:output method="xml" indent="yes"/>
|
||||
|
||||
<xsl:variable name="elts-to-inline">
|
||||
<xsl:text>,access-denied-handler,anonymous,session-management,concurrency-control,after-invocation-provider,authentication-provider,ldap-authentication-provider,user,port-mapping,openid-login,expression-handler,form-login,http-basic,intercept-url,logout,password-encoder,port-mappings,port-mapper,password-compare,protect,protect-pointcut,pre-post-annotation-handling,pre-invocation-advice,post-invocation-advice,invocation-attribute-factory,remember-me,salt-source,x509,</xsl:text>
|
||||
<xsl:text>,access-denied-handler,anonymous,session-management,concurrency-control,after-invocation-provider,authentication-provider,ldap-authentication-provider,user,port-mapping,openid-login,expression-handler,form-login,http-basic,intercept-url,logout,password-encoder,port-mappings,port-mapper,password-compare,protect,protect-pointcut,pre-post-annotation-handling,pre-invocation-advice,post-invocation-advice,invocation-attribute-factory,remember-me,salt-source,x509,add-headers,</xsl:text>
|
||||
</xsl:variable>
|
||||
|
||||
<xsl:template match="xs:element">
|
||||
|
|
|
@ -48,7 +48,7 @@ public class SecurityNamespaceHandlerTests {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void pre31SchemaAreNotSupported() throws Exception {
|
||||
public void pre32SchemaAreNotSupported() throws Exception {
|
||||
try {
|
||||
new InMemoryXmlApplicationContext(
|
||||
"<user-service id='us'>" +
|
||||
|
@ -57,7 +57,7 @@ public class SecurityNamespaceHandlerTests {
|
|||
);
|
||||
fail("Expected BeanDefinitionParsingException");
|
||||
} catch (BeanDefinitionParsingException expected) {
|
||||
assertTrue(expected.getMessage().contains("You cannot use a spring-security-2.0.xsd or"));
|
||||
assertTrue(expected.getMessage().contains("You cannot use a spring-security-2.0.xsd"));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
explaining their purpose. The namespace is written in <link
|
||||
xlink:href="http://www.relaxng.org/">RELAX NG</link> Compact format and later converted into
|
||||
an XSD schema. If you are familiar with this format, you may wish to examine the <link
|
||||
xlink:href="https://fisheye.springsource.org/browse/spring-security/config/src/main/resources/org/springframework/security/config/spring-security-3.1.rnc"
|
||||
xlink:href="https://fisheye.springsource.org/browse/spring-security/config/src/main/resources/org/springframework/security/config/spring-security-3.2.rnc"
|
||||
>schema file</link> directly.</para>
|
||||
<section xml:id="nsa-web">
|
||||
<title>Web Application Security</title>
|
||||
|
@ -210,6 +210,7 @@
|
|||
<title>Child Elements of <http></title>
|
||||
<itemizedlist>
|
||||
<listitem><link xlink:href="#nsa-access-denied-handler">access-denied-handler</link></listitem>
|
||||
<listitem><link xlink:href="#nsa-headers">headers</link></listitem>
|
||||
<listitem><link xlink:href="#nsa-anonymous">anonymous</link></listitem>
|
||||
<listitem><link xlink:href="#nsa-custom-filter">custom-filter</link></listitem>
|
||||
<listitem><link xlink:href="#nsa-expression-handler">expression-handler</link></listitem>
|
||||
|
@ -224,6 +225,7 @@
|
|||
<listitem><link xlink:href="#nsa-request-cache">request-cache</link></listitem>
|
||||
<listitem><link xlink:href="#nsa-session-management">session-management</link></listitem>
|
||||
<listitem><link xlink:href="#nsa-x509">x509</link></listitem>
|
||||
<listitem><link xlink:href="#nsa-headers">headers</link></listitem>
|
||||
</itemizedlist>
|
||||
</section>
|
||||
</section>
|
||||
|
@ -257,6 +259,131 @@
|
|||
</section>
|
||||
</section>
|
||||
</section>
|
||||
<section xml:id="nsa-headers">
|
||||
<title><literal><headers></literal></title>
|
||||
<para>This element allows for configuring additional (security) headers to be send with the response.
|
||||
It enables easy configuration for several headers and also allows for setting custom headers through
|
||||
the <link xlink:href="#nsa-header">header</link> element.
|
||||
<itemizedlist>
|
||||
<listitem><literal>X-Frame-Options</literal> - Can be set using the
|
||||
<link xlink:href="#nsa-frame-options">frame-options</link> element. The
|
||||
<link xlink:href="http://en.wikipedia.org/wiki/Clickjacking#X-Frame-Options">X-Frame-Options
|
||||
</link> header can be used to prevent clickjacking attacks.</listitem>
|
||||
<listitem><literal>X-XSS-Protection</literal> - Can be set using the
|
||||
<link xlink:href="#nsa-xss-protection">xss-protection</link> element.
|
||||
The <link xlink:href="http://en.wikipedia.org/wiki/Cross-site_scripting">X-XSS-Protection
|
||||
</link> header can be used by browser to do basic control.</listitem>
|
||||
<listitem><literal>X-Content-Type-Options</literal> - Can be set using the
|
||||
<link xlink:href="#nsa-content-type-options">content-type-options</link> element. The
|
||||
<link xlink:href="">X-Content-Type-Options</link> header prevents Internet Explorer from
|
||||
MIME-sniffing a response away from the declared content-type. This also applies to Google
|
||||
Chrome, when downloading extensions. </listitem>
|
||||
</itemizedlist>
|
||||
</para>
|
||||
<section xml:id="nsa-headers-parents">
|
||||
<title>Parent Elements of <literal><headers></literal></title>
|
||||
<itemizedlist>
|
||||
<listitem><link xlink:href="#nsa-http">http</link></listitem>
|
||||
</itemizedlist>
|
||||
</section>
|
||||
<section xml:id="nsa-headers-children">
|
||||
<title>Child Elements of <literal><headers></literal></title>
|
||||
<itemizedlist>
|
||||
<listitem><link xlink:href="#nsa-content-type-options">content-type-options</link></listitem>
|
||||
<listitem><link xlink:href="#nsa-frame-options">frame-options</link></listitem>
|
||||
<listitem><link xlink:href="#nsa-header">header</link></listitem>
|
||||
<listitem><link xlink:href="#nsa-xss-protection">xss-protection</link></listitem>
|
||||
</itemizedlist>
|
||||
</section>
|
||||
</section>
|
||||
<section xml:id="nsa-frame-options">
|
||||
<title><literal><frame-options></literal></title>
|
||||
<para>When enabled adds the <link xlink:href="http://tools.ietf.org/html/draft-ietf-websec-x-frame-options-01">X-Frame-Options header</link> to the response, this allows newer browsers to do some security
|
||||
checks and prevent clickjacking attacks.</para>
|
||||
<section xml:id="nsa-frame-options-attributes">
|
||||
<title><literal><frame-options></literal> Attributes</title>
|
||||
<section xml:id="nsa-frame-options-policy">
|
||||
<title><literal>frame-options-policy</literal></title>
|
||||
<para>
|
||||
<itemizedlist>
|
||||
<listitem><literal>DENY</literal> The page cannot be displayed in a frame, regardless of
|
||||
the site attempting to do so. </listitem>
|
||||
<listitem><literal>SAMEORIGIN</literal> The page can only be displayed in a frame on the
|
||||
same origin as the page itself</listitem>
|
||||
<listitem><literal>ALLOW-FROM <link xlink:href="#nsa-frame-options-origin">origin</link></literal>
|
||||
The page can only be displayed in a frame on the specified origin.
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
In other words, if you specify DENY, not only will attempts to load the page in a frame fail
|
||||
when loaded from other sites, attempts to do so will fail when loaded from the same site. On the
|
||||
other hand, if you specify SAMEORIGIN, you can still use the page in a frame as long as the site
|
||||
including it in a frame it is the same as the one serving the page.
|
||||
</para>
|
||||
</section>
|
||||
<section xml:id="nsa-frame-options-origin">
|
||||
<title><literal>frame-options-origin</literal></title>
|
||||
<para>The origin</para>
|
||||
</section>
|
||||
</section>
|
||||
<section xml:id="nsa-frame-options-parents">
|
||||
<title>Parent Elements of <literal><frame-options></literal></title>
|
||||
<itemizedlist>
|
||||
<listitem><link xlink:href="#nsa-headers">headers</link></listitem>
|
||||
</itemizedlist>
|
||||
</section>
|
||||
</section>
|
||||
<section xml:id="nsa-xss-protection">
|
||||
<title><literal><xss-protection></literal></title>
|
||||
<para>Adds the X-XSS-Protection header to the response. This is in no-way a full protection to XSS attacks!</para>
|
||||
<section xml:id="nsa-xss-protection-attributes">
|
||||
<section xml:id="nsa-xss-protection-enabled">
|
||||
<title><literal>xss-protection-enabled</literal></title>
|
||||
<para>Enable or Disable xss-protection.</para>
|
||||
</section>
|
||||
<section xml:id="nsa-xss-protection-block">
|
||||
<title><literal>xss-protection-block</literal></title>
|
||||
<para>When enabled adds mode=block to the header. Which indicates to the browser that loading should be blocked.</para>
|
||||
</section>
|
||||
</section>
|
||||
<section xml:id="nsa-xss-protection-parents">
|
||||
<title>Parent Elements of <literal><xss-protection></literal></title>
|
||||
<itemizedlist>
|
||||
<listitem><link xlink:href="#nsa-headers">headers</link></listitem>
|
||||
</itemizedlist>
|
||||
</section>
|
||||
</section>
|
||||
<section xml:id="nsa-content-type-options">
|
||||
<title><literal><content-type-options></literal></title>
|
||||
<para>Add the X-Content-Type-Options header to the response. Indicates the browser (IE8+) to enable detection
|
||||
for MIME-sniffing.</para>
|
||||
<section xml:id="nsa-content-type-options-parents">
|
||||
<title>Parent Elements of <literal><content-type-options></literal></title>
|
||||
<itemizedlist>
|
||||
<listitem><link xlink:href="#nsa-headers">headers</link></listitem>
|
||||
</itemizedlist>
|
||||
</section>
|
||||
</section>
|
||||
<section xml:id="nsa-header">
|
||||
<title><literal><header></literal></title>
|
||||
<para>Add additional headers to the response, both the name and value need to be specified.</para>
|
||||
<section xml:id="nsa-header-attributes">
|
||||
<title><literal><header-attributes></literal> Attributes</title>
|
||||
<section xml:id="nsa-header-name">
|
||||
<title><literal>header-name</literal></title>
|
||||
<para>The <literal>name</literal> of the header.</para>
|
||||
</section>
|
||||
<section xml:id="nsa-header-value">
|
||||
<title><literal>header-value</literal></title>
|
||||
<para>The <literal>value</literal> of the header to add.</para>
|
||||
</section>
|
||||
</section>
|
||||
<section xml:id="nsa-header-parents">
|
||||
<title>Parent Elements of <literal><header></literal></title>
|
||||
<itemizedlist>
|
||||
<listitem><link xlink:href="#nsa-headers">headers</link></listitem>
|
||||
</itemizedlist>
|
||||
</section>
|
||||
</section>
|
||||
<section xml:id="nsa-anonymous">
|
||||
<title><literal><anonymous></literal></title>
|
||||
<para>Adds an <classname>AnonymousAuthenticationFilter</classname> to the stack and an
|
||||
|
|
|
@ -609,6 +609,23 @@ List<OpenIDAttribute> attributes = token.getAttributes();</programlisting>The
|
|||
MyOpenID providers.</para>
|
||||
</section>
|
||||
</section>
|
||||
<section xml:id="ns-headers">
|
||||
<title>Response Headers</title>
|
||||
<para>A lot of different attacks to hijack content, sessions or connections are available and lately
|
||||
browsers (optionally) can help to prevent those attacks. To enable these features we need to send some
|
||||
additional headers to the client. Spring Security allows for easy configuration for several headers.
|
||||
<progamlisting language="xml">
|
||||
<![CDATA[
|
||||
<headers>
|
||||
<xss-protection/>
|
||||
<frame-options/>
|
||||
<content-type-options/>
|
||||
<header name="foo" value="bar"/>
|
||||
</headers>
|
||||
]]>
|
||||
</progamlisting>
|
||||
</para>
|
||||
</section>
|
||||
<section xml:id="ns-custom-filters">
|
||||
<title>Adding in Your Own Filters</title>
|
||||
<para>If you've used Spring Security before, you'll know that the framework maintains a
|
||||
|
@ -663,6 +680,11 @@ List<OpenIDAttribute> attributes = token.getAttributes();</programlisting>The
|
|||
<entry><literal>ConcurrentSessionFilter</literal> </entry>
|
||||
<entry><literal>session-management/concurrency-control</literal></entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>HEADERS_FILTER</entry>
|
||||
<entry><literal>HeadersFilter</literal> </entry>
|
||||
<entry><literal>http/headers</literal></entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry> LOGOUT_FILTER </entry>
|
||||
<entry><literal>LogoutFilter</literal></entry>
|
||||
|
|
|
@ -30,6 +30,12 @@
|
|||
<http-basic/>
|
||||
<logout logout-success-url="/index.jsp"/>
|
||||
<remember-me />
|
||||
<headers>
|
||||
<xss-protection />
|
||||
<frame-options policy="DENY" />
|
||||
<content-type-options/>
|
||||
<header name="foo" value="bar"/>
|
||||
</headers>
|
||||
<custom-filter ref="switchUserProcessingFilter" position="SWITCH_USER_FILTER"/>
|
||||
</http>
|
||||
|
||||
|
|
|
@ -0,0 +1,62 @@
|
|||
/*
|
||||
* Copyright 2002-2012 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
|
||||
*
|
||||
* http://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.
|
||||
*/
|
||||
package org.springframework.security.web.headers;
|
||||
|
||||
import org.springframework.web.filter.OncePerRequestFilter;
|
||||
|
||||
import javax.servlet.FilterChain;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Filter implementation to add headers to the current request. Can be useful to add certain headers which enable
|
||||
* browser protection. Like X-Frame-Options, X-XSS-Protection and X-Content-Type-Options.
|
||||
*
|
||||
* @author Marten Deinum
|
||||
* @since 3.2
|
||||
*
|
||||
*/
|
||||
public class HeadersFilter extends OncePerRequestFilter {
|
||||
|
||||
/** Map of headers to add to a response */
|
||||
private final Map<String, String> headers = new HashMap<String, String>();
|
||||
|
||||
@Override
|
||||
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
|
||||
for (Map.Entry<String, String> header : headers.entrySet()) {
|
||||
String name = header.getKey();
|
||||
String value = header.getValue();
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("Adding header '" + name + "' with value '"+value +"'");
|
||||
}
|
||||
response.setHeader(header.getKey(), header.getValue());
|
||||
}
|
||||
filterChain.doFilter(request, response);
|
||||
}
|
||||
|
||||
public void setHeaders(Map<String, String> headers) {
|
||||
this.headers.clear();
|
||||
this.headers.putAll(headers);
|
||||
}
|
||||
|
||||
public void addHeader(String name, String value) {
|
||||
headers.put(name, value);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,73 @@
|
|||
/*
|
||||
* Copyright 2002-2013 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
|
||||
*
|
||||
* http://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.
|
||||
*/
|
||||
package org.springframework.security.web.headers;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.mock.web.MockFilterChain;
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.mock.web.MockHttpServletResponse;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.matchers.JUnitMatchers.hasItems;
|
||||
|
||||
/**
|
||||
* Tests for the {@code HeadersFilter}
|
||||
*
|
||||
* @author Marten Deinum
|
||||
* @since 3.2
|
||||
*/
|
||||
public class HeadersFilterTest {
|
||||
|
||||
@Test
|
||||
public void noHeadersConfigured() throws Exception {
|
||||
HeadersFilter filter = new HeadersFilter();
|
||||
MockHttpServletRequest request = new MockHttpServletRequest();
|
||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
MockFilterChain filterChain = new MockFilterChain();
|
||||
|
||||
filter.doFilter(request, response, filterChain);
|
||||
|
||||
assertTrue(response.getHeaderNames().isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void additionalHeadersShouldBeAddedToTheResponse() throws Exception {
|
||||
HeadersFilter filter = new HeadersFilter();
|
||||
Map<String, String> headers = new HashMap<String, String>();
|
||||
headers.put("X-Header1", "foo");
|
||||
headers.put("X-Header2", "bar");
|
||||
filter.setHeaders(headers);
|
||||
|
||||
MockHttpServletRequest request = new MockHttpServletRequest();
|
||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
MockFilterChain filterChain = new MockFilterChain();
|
||||
|
||||
filter.doFilter(request, response, filterChain);
|
||||
|
||||
Collection<String> headerNames = response.getHeaderNames();
|
||||
assertThat(headerNames.size(), is(2));
|
||||
assertThat(headerNames, hasItems("X-Header1", "X-Header2"));
|
||||
assertThat(response.getHeader("X-Header1"), is("foo"));
|
||||
assertThat(response.getHeader("X-Header2"), is("bar"));
|
||||
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue