Browse Source

SEC-2283: Update headers documentation and tests

Rob Winch 12 years ago
parent
commit
d89cf6db29

+ 86 - 1
config/src/main/java/org/springframework/security/config/annotation/web/builders/HttpSecurity.java

@@ -239,6 +239,75 @@ public final class HttpSecurity extends AbstractConfiguredSecurityBuilder<Defaul
         return getOrApply(new OpenIDLoginConfigurer<HttpSecurity>());
         return getOrApply(new OpenIDLoginConfigurer<HttpSecurity>());
     }
     }
 
 
+    /**
+     * Adds the Security headers to the response. This is activated by default
+     * when using {@link WebSecurityConfigurerAdapter}'s default constructor.
+     * Only invoking the {@link #headers()} without invoking additional methods
+     * on it, or accepting the default provided by
+     * {@link WebSecurityConfigurerAdapter}, is the equivalent of:
+     *
+     * <pre>
+     * &#064;Configuration
+     * &#064;EnableWebSecurity
+     * public class CsrfSecurityConfig extends WebSecurityConfigurerAdapter {
+     *
+     * 	&#064;Override
+     *     protected void configure(HttpSecurity http) throws Exception {
+     *         http
+     *             .headers()
+     *                 .contentTypeOptions();
+     *                 .xssProtection()
+     *                 .cacheControl()
+     *                 .httpStrictTransportSecurity()
+     *                 .frameOptions()
+     *                 .and()
+     *             ...;
+     *     }
+     * }
+     * </pre>
+     *
+     * You can disable the headers using the following:
+     *
+     * <pre>
+     * &#064;Configuration
+     * &#064;EnableWebSecurity
+     * public class CsrfSecurityConfig extends WebSecurityConfigurerAdapter {
+     *
+     * 	&#064;Override
+     *     protected void configure(HttpSecurity http) throws Exception {
+     *         http
+     *             .headers().disable()
+     *             ...;
+     *     }
+     * }
+     * </pre>
+     *
+     * You can enable only a few of the headers by invoking the appropriate
+     * methods on {@link #headers()} result. For example, the following will
+     * enable {@link HeadersConfigurer#cacheControl()} and
+     * {@link HeadersConfigurer#frameOptions()} only.
+     *
+     * <pre>
+     * &#064;Configuration
+     * &#064;EnableWebSecurity
+     * public class CsrfSecurityConfig extends WebSecurityConfigurerAdapter {
+     *
+     * 	&#064;Override
+     *     protected void configure(HttpSecurity http) throws Exception {
+     *         http
+     *             .headers()
+     *                 .cacheControl()
+     *                 .frameOptions()
+     *                 .and()
+     *             ...;
+     *     }
+     * }
+     * </pre>
+     *
+     * @return
+     * @throws Exception
+     * @see {@link HeadersConfigurer}
+     */
     public HeadersConfigurer<HttpSecurity> headers() throws Exception {
     public HeadersConfigurer<HttpSecurity> headers() throws Exception {
         return getOrApply(new HeadersConfigurer<HttpSecurity>());
         return getOrApply(new HeadersConfigurer<HttpSecurity>());
     }
     }
@@ -664,7 +733,23 @@ public final class HttpSecurity extends AbstractConfiguredSecurityBuilder<Defaul
 
 
 
 
     /**
     /**
-     * Adds CSRF support
+     * Adds CSRF support. This is activated by default when using
+     * {@link WebSecurityConfigurerAdapter}'s default constructor. You can
+     * disable it using:
+     *
+     * <pre>
+     * &#064;Configuration
+     * &#064;EnableWebSecurity
+     * public class CsrfSecurityConfig extends WebSecurityConfigurerAdapter {
+     *
+     *     &#064;Override
+     *     protected void configure(HttpSecurity http) throws Exception {
+     *         http
+     *             .csrf().disable()
+     *             ...;
+     *     }
+     * }
+     * </pre>
      *
      *
      * @return the {@link ServletApiConfigurer} for further customizations
      * @return the {@link ServletApiConfigurer} for further customizations
      * @throws Exception
      * @throws Exception

+ 109 - 12
config/src/main/java/org/springframework/security/config/annotation/web/configurers/HeadersConfigurer.java

@@ -20,6 +20,7 @@ import java.util.List;
 
 
 import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
 import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
 import org.springframework.security.config.annotation.web.builders.HttpSecurity;
 import org.springframework.security.config.annotation.web.builders.HttpSecurity;
+import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
 import org.springframework.security.web.header.HeaderWriter;
 import org.springframework.security.web.header.HeaderWriter;
 import org.springframework.security.web.header.HeaderWriterFilter;
 import org.springframework.security.web.header.HeaderWriterFilter;
 import org.springframework.security.web.header.writers.CacheControlHeadersWriter;
 import org.springframework.security.web.header.writers.CacheControlHeadersWriter;
@@ -30,15 +31,80 @@ import org.springframework.security.web.header.writers.frameoptions.XFrameOption
 import org.springframework.util.Assert;
 import org.springframework.util.Assert;
 
 
 /**
 /**
+ * Adds the Security headers to the response. This is activated by default when
+ * using {@link WebSecurityConfigurerAdapter}'s default constructor. Only
+ * invoking the {@link #headers()} without invoking additional methods on it, or
+ * accepting the default provided by {@link WebSecurityConfigurerAdapter}, is
+ * the equivalent of:
+ *
+ * <pre>
+ * &#064;Configuration
+ * &#064;EnableWebSecurity
+ * public class CsrfSecurityConfig extends WebSecurityConfigurerAdapter {
+ *
+ * 	&#064;Override
+ *     protected void configure(HttpSecurity http) throws Exception {
+ *         http
+ *             .headers()
+ *                 .contentTypeOptions();
+ *                 .xssProtection()
+ *                 .cacheControl()
+ *                 .httpStrictTransportSecurity()
+ *                 .frameOptions()
+ *                 .and()
+ *             ...;
+ *     }
+ * }
+ * </pre>
+ *
+ * You can disable the headers using the following:
+ *
+ * <pre>
+ * &#064;Configuration
+ * &#064;EnableWebSecurity
+ * public class CsrfSecurityConfig extends WebSecurityConfigurerAdapter {
+ *
+ * 	&#064;Override
+ *     protected void configure(HttpSecurity http) throws Exception {
+ *         http
+ *             .headers().disable()
+ *             ...;
+ *     }
+ * }
+ * </pre>
+ *
+ * You can enable only a few of the headers by invoking the appropriate methods
+ * on {@link #headers()} result. For example, the following will enable
+ * {@link HeadersConfigurer#cacheControl()} and
+ * {@link HeadersConfigurer#frameOptions()} only.
+ *
+ * <pre>
+ * &#064;Configuration
+ * &#064;EnableWebSecurity
+ * public class CsrfSecurityConfig extends WebSecurityConfigurerAdapter {
+ *
+ * 	&#064;Override
+ *     protected void configure(HttpSecurity http) throws Exception {
+ *         http
+ *             .headers()
+ *                 .cacheControl()
+ *                 .frameOptions()
+ *                 .and()
+ *             ...;
+ *     }
+ * }
+ * </pre>
+ *
  * @author Rob Winch
  * @author Rob Winch
  * @since 3.2
  * @since 3.2
- * @see RememberMeConfigurer
  */
  */
-public final class HeadersConfigurer<H extends HttpSecurityBuilder<H>> extends AbstractHttpConfigurer<HeadersConfigurer<H>,H> {
+public final class HeadersConfigurer<H extends HttpSecurityBuilder<H>> extends
+        AbstractHttpConfigurer<HeadersConfigurer<H>, H> {
     private List<HeaderWriter> headerWriters = new ArrayList<HeaderWriter>();
     private List<HeaderWriter> headerWriters = new ArrayList<HeaderWriter>();
 
 
     /**
     /**
      * Creates a new instance
      * Creates a new instance
+     *
      * @see HttpSecurity#headers()
      * @see HttpSecurity#headers()
      */
      */
     public HeadersConfigurer() {
     public HeadersConfigurer() {
@@ -46,7 +112,9 @@ public final class HeadersConfigurer<H extends HttpSecurityBuilder<H>> extends A
 
 
     /**
     /**
      * Adds a {@link HeaderWriter} instance
      * Adds a {@link HeaderWriter} instance
-     * @param headerWriter the {@link HeaderWriter} instance to add
+     *
+     * @param headerWriter
+     *            the {@link HeaderWriter} instance to add
      * @return the {@link HeadersConfigurer} for additional customizations
      * @return the {@link HeadersConfigurer} for additional customizations
      */
      */
     public HeadersConfigurer<H> addHeaderWriter(HeaderWriter headerWriter) {
     public HeadersConfigurer<H> addHeaderWriter(HeaderWriter headerWriter) {
@@ -56,7 +124,13 @@ public final class HeadersConfigurer<H extends HttpSecurityBuilder<H>> extends A
     }
     }
 
 
     /**
     /**
-     * Adds {@link XContentTypeOptionsHeaderWriter}
+     * Adds {@link XContentTypeOptionsHeaderWriter} which inserts the <a href=
+     * "http://msdn.microsoft.com/en-us/library/ie/gg622941(v=vs.85).aspx"
+     * >X-Content-Type-Options</a>:
+     *
+     * <pre>
+     * X-Content-Type-Options: nosniff
+     * </pre>
      *
      *
      * @return the {@link HeadersConfigurer} for additional customizations
      * @return the {@link HeadersConfigurer} for additional customizations
      */
      */
@@ -65,8 +139,11 @@ public final class HeadersConfigurer<H extends HttpSecurityBuilder<H>> extends A
     }
     }
 
 
     /**
     /**
-     * Adds {@link XXssProtectionHeaderWriter}. Note this is not comprehensive
-     * XSS protection!
+     * <strong>Note this is not comprehensive XSS protection!</strong>
+     *
+     * <para>Adds {@link XXssProtectionHeaderWriter} which adds the <a href=
+     * "http://blogs.msdn.com/b/ieinternals/archive/2011/01/31/controlling-the-internet-explorer-xss-filter-with-the-x-xss-protection-http-header.aspx"
+     * >X-XSS-Protection header</a>
      *
      *
      * @return the {@link HeadersConfigurer} for additional customizations
      * @return the {@link HeadersConfigurer} for additional customizations
      */
      */
@@ -75,7 +152,12 @@ public final class HeadersConfigurer<H extends HttpSecurityBuilder<H>> extends A
     }
     }
 
 
     /**
     /**
-     * Adds {@link CacheControlHeadersWriter}.
+     * Adds {@link CacheControlHeadersWriter}. Specifically it adds the
+     * following headers:
+     * <ul>
+     * <li>Cache-Control: no-cache, no-store, max-age=0, must-revalidate</li>
+     * <li>Pragma: no-cache</li>
+     * </ul>
      *
      *
      * @return the {@link HeadersConfigurer} for additional customizations
      * @return the {@link HeadersConfigurer} for additional customizations
      */
      */
@@ -84,7 +166,15 @@ public final class HeadersConfigurer<H extends HttpSecurityBuilder<H>> extends A
     }
     }
 
 
     /**
     /**
-     * Adds {@link HstsHeaderWriter}.
+     * Adds {@link HstsHeaderWriter} which provides support for <a
+     * href="http://tools.ietf.org/html/rfc6797">HTTP Strict Transport Security
+     * (HSTS)</a>.
+     *
+     * <p>
+     * For additional configuration options, use
+     * {@link #addHeaderWriter(HeaderWriter)} and {@link HstsHeaderWriter}
+     * directly.
+     * </p>
      *
      *
      * @return the {@link HeadersConfigurer} for additional customizations
      * @return the {@link HeadersConfigurer} for additional customizations
      */
      */
@@ -93,7 +183,10 @@ public final class HeadersConfigurer<H extends HttpSecurityBuilder<H>> extends A
     }
     }
 
 
     /**
     /**
-     * Adds {@link XFrameOptionsHeaderWriter} with all the default settings.
+     * Adds {@link XFrameOptionsHeaderWriter} with all the default settings. For
+     * additional configuration options, use
+     * {@link #addHeaderWriter(HeaderWriter)} and
+     * {@link XFrameOptionsHeaderWriter} directly.
      *
      *
      * @return the {@link HeadersConfigurer} for additional customizations
      * @return the {@link HeadersConfigurer} for additional customizations
      */
      */
@@ -109,20 +202,24 @@ public final class HeadersConfigurer<H extends HttpSecurityBuilder<H>> extends A
 
 
     /**
     /**
      * Creates the {@link HeaderWriter}
      * Creates the {@link HeaderWriter}
+     *
      * @return the {@link HeaderWriter}
      * @return the {@link HeaderWriter}
      */
      */
     private HeaderWriterFilter createHeaderWriterFilter() {
     private HeaderWriterFilter createHeaderWriterFilter() {
-        HeaderWriterFilter headersFilter = new HeaderWriterFilter(getHeaderWriters());
+        HeaderWriterFilter headersFilter = new HeaderWriterFilter(
+                getHeaderWriters());
         headersFilter = postProcess(headersFilter);
         headersFilter = postProcess(headersFilter);
         return headersFilter;
         return headersFilter;
     }
     }
 
 
     /**
     /**
-     * Gets the {@link HeaderWriter} instances and possibly initializes with the defaults.
+     * Gets the {@link HeaderWriter} instances and possibly initializes with the
+     * defaults.
+     *
      * @return
      * @return
      */
      */
     private List<HeaderWriter> getHeaderWriters() {
     private List<HeaderWriter> getHeaderWriters() {
-        if(headerWriters.isEmpty()) {
+        if (headerWriters.isEmpty()) {
             addDefaultHeaderWriters();
             addDefaultHeaderWriters();
         }
         }
         return headerWriters;
         return headerWriters;

+ 162 - 0
config/src/test/groovy/org/springframework/security/config/annotation/web/configurers/HeadersConfigurerTests.groovy

@@ -0,0 +1,162 @@
+/*
+ * 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.config.annotation.web.configurers
+
+import javax.servlet.http.HttpServletResponse
+
+import org.springframework.context.annotation.Configuration
+import org.springframework.security.config.annotation.BaseSpringSpec
+import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder
+import org.springframework.security.config.annotation.web.builders.HttpSecurity
+import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity
+import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter
+import org.springframework.security.web.access.AccessDeniedHandler
+import org.springframework.security.web.csrf.CsrfFilter;
+import org.springframework.security.web.csrf.CsrfTokenRepository;
+import org.springframework.security.web.servlet.support.csrf.CsrfRequestDataValueProcessor;
+import org.springframework.security.web.util.RequestMatcher;
+import org.springframework.web.servlet.support.RequestDataValueProcessor;
+
+import spock.lang.Unroll;
+
+/**
+ *
+ * @author Rob Winch
+ */
+class HeadersConfigurerTests extends BaseSpringSpec {
+
+    def "headers"() {
+        setup:
+            loadConfig(HeadersConfig)
+            request.secure = true
+        when:
+            springSecurityFilterChain.doFilter(request,response,chain)
+        then:
+            responseHeaders == ['X-Content-Type-Options':'nosniff',
+                         'X-Frame-Options':'DENY',
+                         'Strict-Transport-Security': 'max-age=31536000 ; includeSubDomains',
+                         'Cache-Control': 'no-cache,no-store,max-age=0,must-revalidate',
+                         'Pragma':'no-cache',
+                         'X-XSS-Protection' : '1; mode=block']
+    }
+
+    @Configuration
+    @EnableWebSecurity
+    static class HeadersConfig extends WebSecurityConfigurerAdapter {
+
+        @Override
+        protected void configure(HttpSecurity http) throws Exception {
+            http.headers()
+        }
+    }
+
+    def "headers.contentType"() {
+        setup:
+            loadConfig(ContentTypeOptionsConfig)
+        when:
+            springSecurityFilterChain.doFilter(request,response,chain)
+        then:
+            responseHeaders == ['X-Content-Type-Options':'nosniff']
+    }
+
+    @Configuration
+    @EnableWebSecurity
+    static class ContentTypeOptionsConfig extends WebSecurityConfigurerAdapter {
+
+        @Override
+        protected void configure(HttpSecurity http) throws Exception {
+            http.headers().contentTypeOptions()
+        }
+    }
+
+    def "headers.frameOptions"() {
+        setup:
+            loadConfig(FrameOptionsConfig)
+        when:
+            springSecurityFilterChain.doFilter(request,response,chain)
+        then:
+            responseHeaders == ['X-Frame-Options':'DENY']
+    }
+
+    @Configuration
+    @EnableWebSecurity
+    static class FrameOptionsConfig extends WebSecurityConfigurerAdapter {
+
+        @Override
+        protected void configure(HttpSecurity http) throws Exception {
+            http.headers().frameOptions()
+        }
+    }
+
+    def "headers.hsts"() {
+        setup:
+            loadConfig(HstsConfig)
+            request.secure = true
+        when:
+            springSecurityFilterChain.doFilter(request,response,chain)
+        then:
+            responseHeaders == ['Strict-Transport-Security': 'max-age=31536000 ; includeSubDomains']
+    }
+
+    @Configuration
+    @EnableWebSecurity
+    static class HstsConfig extends WebSecurityConfigurerAdapter {
+
+        @Override
+        protected void configure(HttpSecurity http) throws Exception {
+            http.headers().httpStrictTransportSecurity()
+        }
+    }
+
+    def "headers.cacheControl"() {
+        setup:
+            loadConfig(CacheControlConfig)
+        when:
+            springSecurityFilterChain.doFilter(request,response,chain)
+        then:
+            responseHeaders == ['Cache-Control': 'no-cache,no-store,max-age=0,must-revalidate',
+                         'Pragma':'no-cache']
+    }
+
+    @Configuration
+    @EnableWebSecurity
+    static class CacheControlConfig extends WebSecurityConfigurerAdapter {
+
+        @Override
+        protected void configure(HttpSecurity http) throws Exception {
+            http.headers().cacheControl()
+        }
+    }
+
+    def "headers.xssProtection"() {
+        setup:
+            loadConfig(XssProtectionConfig)
+        when:
+            springSecurityFilterChain.doFilter(request,response,chain)
+        then:
+            responseHeaders == ['X-XSS-Protection' : '1; mode=block']
+    }
+
+    @Configuration
+    @EnableWebSecurity
+    static class XssProtectionConfig extends WebSecurityConfigurerAdapter {
+
+        @Override
+        protected void configure(HttpSecurity http) throws Exception {
+            http.headers().xssProtection()
+        }
+    }
+}

+ 5 - 5
docs/manual/src/docbook/appendix-namespace.xml

@@ -354,7 +354,7 @@
             <section xml:id="nsa-frame-options-attributes">
             <section xml:id="nsa-frame-options-attributes">
                 <title><literal>&lt;frame-options&gt;</literal> Attributes</title>
                 <title><literal>&lt;frame-options&gt;</literal> Attributes</title>
                 <section xml:id="nsa-frame-options-policy">
                 <section xml:id="nsa-frame-options-policy">
-                    <title><literal>frame-options-policy</literal></title>
+                    <title><literal>policy</literal></title>
                     <para>
                     <para>
                         <itemizedlist>
                         <itemizedlist>
                             <listitem><literal>DENY</literal> The page cannot be displayed in a frame, regardless of
                             <listitem><literal>DENY</literal> The page cannot be displayed in a frame, regardless of
@@ -372,7 +372,7 @@
                     </para>
                     </para>
                 </section>
                 </section>
                 <section xml:id="nsa-frame-options-strategy">
                 <section xml:id="nsa-frame-options-strategy">
-                    <title><literal>frame-options-strategy</literal></title>
+                    <title><literal>strategy</literal></title>
                     <para>
                     <para>
                         Select the <classname>AllowFromStrategy</classname> to use when using the ALLOW-FROM policy.
                         Select the <classname>AllowFromStrategy</classname> to use when using the ALLOW-FROM policy.
                         <itemizedlist>
                         <itemizedlist>
@@ -393,18 +393,18 @@
                     </para>
                     </para>
                 </section>
                 </section>
                 <section xml:id="nsa-frame-options-ref">
                 <section xml:id="nsa-frame-options-ref">
-                    <title><literal>frame-options-ref</literal></title>
+                    <title><literal>ref</literal></title>
                     <para>
                     <para>
                         Instead of using one of the predefined strategies it is also possible to use a custom <classname>AllowFromStrategy</classname>.
                         Instead of using one of the predefined strategies it is also possible to use a custom <classname>AllowFromStrategy</classname>.
                         The reference to this bean can be specified through this ref attribute.
                         The reference to this bean can be specified through this ref attribute.
                     </para>
                     </para>
                 </section>
                 </section>
                 <section xml:id="nsa-frame-options-value">
                 <section xml:id="nsa-frame-options-value">
-                    <title><literal>frame-options-value</literal></title>
+                    <title><literal>value</literal></title>
                     <para>The value to use when ALLOW-FROM is used a <link linkend="nsa-frame-options-strategy">strategy</link>.</para>
                     <para>The value to use when ALLOW-FROM is used a <link linkend="nsa-frame-options-strategy">strategy</link>.</para>
                 </section>
                 </section>
                 <section xml:id="nsa-frame-options-from-parameter">
                 <section xml:id="nsa-frame-options-from-parameter">
-                    <title><literal>frame-options-from-parameter</literal></title>
+                    <title><literal>from-parameter</literal></title>
                     <para>
                     <para>
                         Specify the name of the request parameter to use when using regexp or whitelist for the ALLOW-FROM
                         Specify the name of the request parameter to use when using regexp or whitelist for the ALLOW-FROM
                         strategy.
                         strategy.

+ 72 - 2
docs/manual/src/docbook/headers.xml

@@ -205,7 +205,8 @@ public class WebSecurityConfig extends
         <note>
         <note>
             <para>Another modern approach to dealing with clickjacking is using a <link xlink:href="http://www.w3.org/TR/CSP/">Content
             <para>Another modern approach to dealing with clickjacking is using a <link xlink:href="http://www.w3.org/TR/CSP/">Content
                 Security Policy</link>. Spring Security does not provide
                 Security Policy</link>. Spring Security does not provide
-                support for this as the specification is not released and it is quite a bit more complicated. To stay up to date with this
+                support for this as the specification is not released and it is quite a bit more complicated. However, you could use the
+                <link linkend="headers-static">static headers</link> feature to implement this. To stay up to date with this
                 issue and to see how you can implement it with Spring Security refer to
                 issue and to see how you can implement it with Spring Security refer to
                 <link xlink:href="https://jira.springsource.org/browse/SEC-2117">SEC-2117</link> </para>
                 <link xlink:href="https://jira.springsource.org/browse/SEC-2117">SEC-2117</link> </para>
         </note>
         </note>
@@ -242,7 +243,7 @@ public class WebSecurityConfig extends
   }
   }
 }]]></programlisting>
 }]]></programlisting>
     </section>
     </section>
-    <section xml:id="xss-protection">
+    <section xml:id="headers-xss-protection">
         <title>X-XSS-Protection</title>
         <title>X-XSS-Protection</title>
         <para>Some browsers have built in support for filtering out
         <para>Some browsers have built in support for filtering out
             <link xlink:href="https://www.owasp.org/index.php/Testing_for_Reflected_Cross_site_scripting_(OWASP-DV-001)">reflected
             <link xlink:href="https://www.owasp.org/index.php/Testing_for_Reflected_Cross_site_scripting_(OWASP-DV-001)">reflected
@@ -276,6 +277,75 @@ public class WebSecurityConfig extends
         .and()
         .and()
       ...;
       ...;
   }
   }
+}]]></programlisting>
+    </section>
+    <section xml:id="headers-static">
+        <title>Static Headers</title>
+        <para>There may be times you wish to inject custom security headers into your application that are not supported out of the box. For example, perhaps
+            you wish to have early support for <link xlink:href="http://www.w3.org/TR/CSP/">Content Security Policy</link> in order to ensure that resources
+            are only loaded from the same origin. Since support for Content Security Policy has not been finalized, browsers use one of two common extension headers
+            to implement the feature. This means we will need to inject the policy twice. An example of the headers can be seen below:</para>
+            <programlisting><![CDATA[X-Content-Security-Policy: default-src 'self'
+X-WebKit-CSP: default-src 'self']]></programlisting>
+            <para>When using the XML namespace, these headers can be added to the response using the <link linkend="nsa-header">&lt;header&gt;</link> element as
+            shown below:</para>
+        <programlisting language="xml"><![CDATA[<http ...>
+    ...
+    <headers>
+        <header name="X-Content-Security-Policy" value="default-src 'self'"/>
+        <header name="X-WebKit-CSP" value="default-src 'self'"/>
+    </headers>
+</http>]]></programlisting>
+        <para>Similarly, the headers could be added to the response using Java Configuration as shown in the following:</para>
+        <programlisting language="java"><![CDATA[@EnableWebSecurity
+@Configuration
+public class WebSecurityConfig extends
+   WebSecurityConfigurerAdapter {
+
+  @Override
+  protected void configure(HttpSecurity http) throws Exception {
+    http
+      .headers()
+        .addHeaderWriter(new StaticHeaderWriter("X-Content-Security-Policy","default-src 'self'"))
+        .addHeaderWriter(new StaticHeaderWriter("X-WebKit-CSP","default-src 'self'"))
+        .and()
+      ...;
+  }
+}]]></programlisting>
+    </section>
+    <section xml:id="headers-writer">
+        <title>Headers Writer</title>
+        <para>When the namespace or Java configuration does not support the headers you want, you can create a custom <interfacename>HeadersWriter</interfacename> instance
+            or even provide a custom implementation of the <interfacename>HeadersWriter</interfacename>.</para>
+        <para>Let's take a look at an example of using an custom instance of <classname>XFrameOptionsHeaderWriter</classname>. Perhaps you want to allow framing of content
+            for the same origin. This is easily supported by setting the <link linkend="nsa-frame-options-policy">policy</link>
+            attribute to "SAMEORIGIN", but let's take a look at a more explicit example.</para>
+            <programlisting language="xml"><![CDATA[<http ...>
+    ...
+    <headers>
+        <header header-ref="frameOptionsWriter"/>
+    </headers>
+</http>
+<!-- Requires the c-namespace.
+  See http://static.springsource.org/spring/docs/3.2.x/spring-framework-reference/html/beans.html#beans-c-namespace
+-->
+<bean:bean id="frameOptionsWriter"
+    class="org.springframework.security.web.header.writers.frameoptions.XFrameOptionsHeaderWriter"
+    c:frameOptionsMode="SAMEORIGIN"/>]]></programlisting>
+    <para>We could also restrict framing of content to the same origin with Java configuration:</para>
+        <programlisting language="java"><![CDATA[@EnableWebSecurity
+@Configuration
+public class WebSecurityConfig extends
+   WebSecurityConfigurerAdapter {
+
+  @Override
+  protected void configure(HttpSecurity http) throws Exception {
+    http
+      .headers()
+        .addHeaderWriter(new XFrameOptionsHeaderWriter(XFrameOptionsMode.SAMEORIGIN))
+        .and()
+      ...;
+  }
 }]]></programlisting>
 }]]></programlisting>
     </section>
     </section>