13. Protection Against Exploits

13.1 Cross Site Request Forgery (CSRF)

This section discusses Spring Security’s Cross Site Request Forgery (CSRF) support.

13.1.1 CSRF Attacks

Before we discuss how Spring Security can protect applications from CSRF attacks, we will explain what a CSRF attack is. Let’s take a look at a concrete example to get a better understanding.

Assume that your bank’s website provides a form that allows transferring money from the currently logged in user to another bank account. For example, the HTTP request might look like:

POST /transfer HTTP/1.1
Host: bank.example.com
Cookie: JSESSIONID=randomid; Domain=bank.example.com; Secure; HttpOnly
Content-Type: application/x-www-form-urlencoded

amount=100.00&routingNumber=1234&account=9876

Now pretend you authenticate to your bank’s website and then, without logging out, visit an evil website. The evil website contains an HTML page with the following form:

<form action="https://bank.example.com/transfer" method="post">
<input type="hidden"
    name="amount"
    value="100.00"/>
<input type="hidden"
    name="routingNumber"
    value="evilsRoutingNumber"/>
<input type="hidden"
    name="account"
    value="evilsAccountNumber"/>
<input type="submit"
    value="Win Money!"/>
</form>

You like to win money, so you click on the submit button. In the process, you have unintentionally transferred $100 to a malicious user. This happens because, while the evil website cannot see your cookies, the cookies associated with your bank are still sent along with the request.

Worst yet, this whole process could have been automated using JavaScript. This means you didn’t even need to click on the button. So how do we protect ourselves from such attacks?

13.1.2 Synchronizer Token Pattern

The issue is that the HTTP request from the bank’s website and the request from the evil website are exactly the same. This means there is no way to reject requests coming from the evil website and allow requests coming from the bank’s website. To protect against CSRF attacks we need to ensure there is something in the request that the evil site is unable to provide.

One solution is to use the Synchronizer Token Pattern. This solution is to ensure that each request requires, in addition to our session cookie, a randomly generated token as an HTTP parameter. When a request is submitted, the server must look up the expected value for the parameter and compare it against the actual value in the request. If the values do not match, the request should fail.

We can relax the expectations to only require the token for each HTTP request that updates state. This can be safely done since the same origin policy ensures the evil site cannot read the response. Additionally, we do not want to include the random token in HTTP GET as this can cause the tokens to be leaked.

Let’s take a look at how our example would change. Assume the randomly generated token is present in an HTTP parameter named _csrf. For example, the request to transfer money would look like this:

POST /transfer HTTP/1.1
Host: bank.example.com
Cookie: JSESSIONID=randomid; Domain=bank.example.com; Secure; HttpOnly
Content-Type: application/x-www-form-urlencoded

amount=100.00&routingNumber=1234&account=9876&_csrf=<secure-random>

You will notice that we added the _csrf parameter with a random value. Now the evil website will not be able to guess the correct value for the _csrf parameter (which must be explicitly provided on the evil website) and the transfer will fail when the server compares the actual token to the expected token.

13.1.3 When to use CSRF protection

When should you use CSRF protection? Our recommendation is to use CSRF protection for any request that could be processed by a browser by normal users. If you are only creating a service that is used by non-browser clients, you will likely want to disable CSRF protection.

CSRF protection and JSON

A common question is "do I need to protect JSON requests made by javascript?" The short answer is, it depends. However, you must be very careful as there are CSRF exploits that can impact JSON requests. For example, a malicious user can create a CSRF with JSON using the following form:

<form action="https://bank.example.com/transfer" method="post" enctype="text/plain">
<input name='{"amount":100,"routingNumber":"evilsRoutingNumber","account":"evilsAccountNumber", "ignore_me":"' value='test"}' type='hidden'>
<input type="submit"
    value="Win Money!"/>
</form>

This will produce the following JSON structure

{ "amount": 100,
"routingNumber": "evilsRoutingNumber",
"account": "evilsAccountNumber",
"ignore_me": "=test"
}

If an application were not validating the Content-Type, then it would be exposed to this exploit. Depending on the setup, a Spring MVC application that validates the Content-Type could still be exploited by updating the URL suffix to end with ".json" as shown below:

<form action="https://bank.example.com/transfer.json" method="post" enctype="text/plain">
<input name='{"amount":100,"routingNumber":"evilsRoutingNumber","account":"evilsAccountNumber", "ignore_me":"' value='test"}' type='hidden'>
<input type="submit"
    value="Win Money!"/>
</form>

CSRF and Stateless Browser Applications

What if my application is stateless? That doesn’t necessarily mean you are protected. In fact, if a user does not need to perform any actions in the web browser for a given request, they are likely still vulnerable to CSRF attacks.

For example, consider an application uses a custom cookie that contains all the state within it for authentication instead of the JSESSIONID. When the CSRF attack is made the custom cookie will be sent with the request in the same manner that the JSESSIONID cookie was sent in our previous example.

Users using basic authentication are also vulnerable to CSRF attacks since the browser will automatically include the username password in any requests in the same manner that the JSESSIONID cookie was sent in our previous example.

13.1.4 Using Spring Security CSRF Protection

So what are the steps necessary to use Spring Security’s to protect our site against CSRF attacks? The steps to using Spring Security’s CSRF protection are outlined below:

Use proper HTTP verbs

The first step to protecting against CSRF attacks is to ensure your website uses proper HTTP verbs. Specifically, before Spring Security’s CSRF support can be of use, you need to be certain that your application is using PATCH, POST, PUT, and/or DELETE for anything that modifies state.

This is not a limitation of Spring Security’s support, but instead a general requirement for proper CSRF prevention. The reason is that including private information in an HTTP GET can cause the information to be leaked. See RFC 2616 Section 15.1.3 Encoding Sensitive Information in URI’s for general guidance on using POST instead of GET for sensitive information.

Configure CSRF Protection

The next step is to include Spring Security’s CSRF protection within your application. Some frameworks handle invalid CSRF tokens by invaliding the user’s session, but this causes its own problems. Instead by default Spring Security’s CSRF protection will produce an HTTP 403 access denied. This can be customized by configuring the AccessDeniedHandler to process InvalidCsrfTokenException differently.

As of Spring Security 4.0, CSRF protection is enabled by default with XML configuration. If you would like to disable CSRF protection, the corresponding XML configuration can be seen below.

<http>
    <!-- ... -->
    <csrf disabled="true"/>
</http>

CSRF protection is enabled by default with Java Configuration. If you would like to disable CSRF, the corresponding Java configuration can be seen below. Refer to the Javadoc of csrf() for additional customizations in how CSRF protection is configured.

@EnableWebSecurity
public class WebSecurityConfig extends
WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .csrf(csrf ->
                csrf.disable()
            );
    }
}

Include the CSRF Token

Form Submissions

The last step is to ensure that you include the CSRF token in all PATCH, POST, PUT, and DELETE methods. One way to approach this is to use the _csrf request attribute to obtain the current CsrfToken. An example of doing this with a JSP is shown below:

<c:url var="logoutUrl" value="/logout"/>
<form action="${logoutUrl}"
    method="post">
<input type="submit"
    value="Log out" />
<input type="hidden"
    name="${_csrf.parameterName}"
    value="${_csrf.token}"/>
</form>

An easier approach is to use the csrfInput tag from the Spring Security JSP tag library.

[Note]Note

If you are using Spring MVC <form:form> tag or Thymeleaf 2.1+ and are using @EnableWebSecurity, the CsrfToken is automatically included for you (using the CsrfRequestDataValueProcessor).

Ajax and JSON Requests

If you are using JSON, then it is not possible to submit the CSRF token within an HTTP parameter. Instead you can submit the token within a HTTP header. A typical pattern would be to include the CSRF token within your meta tags. An example with a JSP is shown below:

<html>
<head>
    <meta name="_csrf" content="${_csrf.token}"/>
    <!-- default header name is X-CSRF-TOKEN -->
    <meta name="_csrf_header" content="${_csrf.headerName}"/>
    <!-- ... -->
</head>
<!-- ... -->

Instead of manually creating the meta tags, you can use the simpler csrfMetaTags tag from the Spring Security JSP tag library.

You can then include the token within all your Ajax requests. If you were using jQuery, this could be done with the following:

$(function () {
var token = $("meta[name='_csrf']").attr("content");
var header = $("meta[name='_csrf_header']").attr("content");
$(document).ajaxSend(function(e, xhr, options) {
    xhr.setRequestHeader(header, token);
});
});

As an alternative to jQuery, we recommend using cujoJS’s rest.js. The rest.js module provides advanced support for working with HTTP requests and responses in RESTful ways. A core capability is the ability to contextualize the HTTP client adding behavior as needed by chaining interceptors on to the client.

var client = rest.chain(csrf, {
token: $("meta[name='_csrf']").attr("content"),
name: $("meta[name='_csrf_header']").attr("content")
});

The configured client can be shared with any component of the application that needs to make a request to the CSRF protected resource. One significant difference between rest.js and jQuery is that only requests made with the configured client will contain the CSRF token, vs jQuery where all requests will include the token. The ability to scope which requests receive the token helps guard against leaking the CSRF token to a third party. Please refer to the rest.js reference documentation for more information on rest.js.

CookieCsrfTokenRepository

There can be cases where users will want to persist the CsrfToken in a cookie. By default the CookieCsrfTokenRepository will write to a cookie named XSRF-TOKEN and read it from a header named X-XSRF-TOKEN or the HTTP parameter _csrf. These defaults come from AngularJS

You can configure CookieCsrfTokenRepository in XML using the following:

<http>
    <!-- ... -->
    <csrf token-repository-ref="tokenRepository"/>
</http>
<b:bean id="tokenRepository"
    class="org.springframework.security.web.csrf.CookieCsrfTokenRepository"
    p:cookieHttpOnly="false"/>
[Note]Note

The sample explicitly sets cookieHttpOnly=false. This is necessary to allow JavaScript (i.e. AngularJS) to read it. If you do not need the ability to read the cookie with JavaScript directly, it is recommended to omit cookieHttpOnly=false to improve security.

You can configure CookieCsrfTokenRepository in Java Configuration using:

@EnableWebSecurity
public class WebSecurityConfig extends
        WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .csrf(csrf ->
                csrf
                    .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
            );
    }
}
[Note]Note

The sample explicitly sets cookieHttpOnly=false. This is necessary to allow JavaScript (i.e. AngularJS) to read it. If you do not need the ability to read the cookie with JavaScript directly, it is recommended to omit cookieHttpOnly=false (by using new CookieCsrfTokenRepository() instead) to improve security.

13.1.5 CSRF Caveats

There are a few caveats when implementing CSRF.

Timeouts

One issue is that the expected CSRF token is stored in the HttpSession, so as soon as the HttpSession expires your configured AccessDeniedHandler will receive a InvalidCsrfTokenException. If you are using the default AccessDeniedHandler, the browser will get an HTTP 403 and display a poor error message.

[Note]Note

One might ask why the expected CsrfToken isn’t stored in a cookie by default. This is because there are known exploits in which headers (i.e. specify the cookies) can be set by another domain. This is the same reason Ruby on Rails no longer skips CSRF checks when the header X-Requested-With is present. See this webappsec.org thread for details on how to perform the exploit. Another disadvantage is that by removing the state (i.e. the timeout) you lose the ability to forcibly terminate the token if it is compromised.

A simple way to mitigate an active user experiencing a timeout is to have some JavaScript that lets the user know their session is about to expire. The user can click a button to continue and refresh the session.

Alternatively, specifying a custom AccessDeniedHandler allows you to process the InvalidCsrfTokenException any way you like. For an example of how to customize the AccessDeniedHandler refer to the provided links for both xml and Java configuration.

Finally, the application can be configured to use CookieCsrfTokenRepository which will not expire. As previously mentioned, this is not as secure as using a session, but in many cases can be good enough.

Logging In

In order to protect against forging log in requests the log in form should be protected against CSRF attacks too. Since the CsrfToken is stored in HttpSession, this means an HttpSession will be created as soon as CsrfToken token attribute is accessed. While this sounds bad in a RESTful / stateless architecture the reality is that state is necessary to implement practical security. Without state, we have nothing we can do if a token is compromised. Practically speaking, the CSRF token is quite small in size and should have a negligible impact on our architecture.

A common technique to protect the log in form is by using a JavaScript function to obtain a valid CSRF token before the form submission. By doing this, there is no need to think about session timeouts (discussed in the previous section) because the session is created right before the form submission (assuming that CookieCsrfTokenRepository isn’t configured instead), so the user can stay on the login page and submit the username/password when he wants. In order to achieve this, you can take advantage of the CsrfTokenArgumentResolver provided by Spring Security and expose an endpoint like it’s described on here.

Logging Out

Adding CSRF will update the LogoutFilter to only use HTTP POST. This ensures that log out requires a CSRF token and that a malicious user cannot forcibly log out your users.

One approach is to use a form for log out. If you really want a link, you can use JavaScript to have the link perform a POST (i.e. maybe on a hidden form). For browsers with JavaScript that is disabled, you can optionally have the link take the user to a log out confirmation page that will perform the POST.

If you really want to use HTTP GET with logout you can do so, but remember this is generally not recommended. For example, the following Java Configuration will perform logout with the URL /logout is requested with any HTTP method:

@EnableWebSecurity
public class WebSecurityConfig extends
WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .logout(logout ->
                logout
                    .logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
            );
    }
}

Multipart (file upload)

There are two options to using CSRF protection with multipart/form-data. Each option has its tradeoffs.

[Note]Note

Before you integrate Spring Security’s CSRF protection with multipart file upload, ensure that you can upload without the CSRF protection first. More information about using multipart forms with Spring can be found within the 17.10 Spring’s multipart (file upload) support section of the Spring reference and the MultipartFilter javadoc.

Placing MultipartFilter before Spring Security

The first option is to ensure that the MultipartFilter is specified before the Spring Security filter. Specifying the MultipartFilter before the Spring Security filter means that there is no authorization for invoking the MultipartFilter which means anyone can place temporary files on your server. However, only authorized users will be able to submit a File that is processed by your application. In general, this is the recommended approach because the temporary file upload should have a negligble impact on most servers.

To ensure MultipartFilter is specified before the Spring Security filter with java configuration, users can override beforeSpringSecurityFilterChain as shown below:

public class SecurityApplicationInitializer extends AbstractSecurityWebApplicationInitializer {

    @Override
    protected void beforeSpringSecurityFilterChain(ServletContext servletContext) {
        insertFilters(servletContext, new MultipartFilter());
    }
}

To ensure MultipartFilter is specified before the Spring Security filter with XML configuration, users can ensure the <filter-mapping> element of the MultipartFilter is placed before the springSecurityFilterChain within the web.xml as shown below:

<filter>
    <filter-name>MultipartFilter</filter-name>
    <filter-class>org.springframework.web.multipart.support.MultipartFilter</filter-class>
</filter>
<filter>
    <filter-name>springSecurityFilterChain</filter-name>
    <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
    <filter-name>MultipartFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>
<filter-mapping>
    <filter-name>springSecurityFilterChain</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>
Include CSRF token in action

If allowing unauthorized users to upload temporariy files is not acceptable, an alternative is to place the MultipartFilter after the Spring Security filter and include the CSRF as a query parameter in the action attribute of the form. An example with a jsp is shown below

<form action="./upload?${_csrf.parameterName}=${_csrf.token}" method="post" enctype="multipart/form-data">

The disadvantage to this approach is that query parameters can be leaked. More genearlly, it is considered best practice to place sensitive data within the body or headers to ensure it is not leaked. Additional information can be found in RFC 2616 Section 15.1.3 Encoding Sensitive Information in URI’s.

HiddenHttpMethodFilter

The HiddenHttpMethodFilter should be placed before the Spring Security filter. In general this is true, but it could have additional implications when protecting against CSRF attacks.

Note that the HiddenHttpMethodFilter only overrides the HTTP method on a POST, so this is actually unlikely to cause any real problems. However, it is still best practice to ensure it is placed before Spring Security’s filters.

13.1.6 Overriding Defaults

Spring Security’s goal is to provide defaults that protect your users from exploits. This does not mean that you are forced to accept all of its defaults.

For example, you can provide a custom CsrfTokenRepository to override the way in which the CsrfToken is stored.

You can also specify a custom RequestMatcher to determine which requests are protected by CSRF (i.e. perhaps you don’t care if log out is exploited). In short, if Spring Security’s CSRF protection doesn’t behave exactly as you want it, you are able to customize the behavior. Refer to the the section called “<csrf>” documentation for details on how to make these customizations with XML and the CsrfConfigurer javadoc for details on how to make these customizations when using Java configuration.

13.2 Security HTTP Response Headers

This section discusses Spring Security’s support for adding various security headers to the response.

13.2.1 Default Security Headers

Spring Security allows users to easily inject the default security headers to assist in protecting their application. The default for Spring Security is to include the following headers:

Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Pragma: no-cache
Expires: 0
X-Content-Type-Options: nosniff
Strict-Transport-Security: max-age=31536000 ; includeSubDomains
X-Frame-Options: DENY
X-XSS-Protection: 1; mode=block
[Note]Note

Strict-Transport-Security is only added on HTTPS requests

For additional details on each of these headers, refer to the corresponding sections:

While each of these headers are considered best practice, it should be noted that not all clients utilize the headers, so additional testing is encouraged.

You can customize specific headers. For example, assume that want your HTTP response headers to look like the following:

Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Pragma: no-cache
Expires: 0
X-Content-Type-Options: nosniff
X-Frame-Options: SAMEORIGIN
X-XSS-Protection: 1; mode=block

Specifically, you want all of the default headers with the following customizations:

You can easily do this with the following Java Configuration:

@EnableWebSecurity
public class WebSecurityConfig extends
        WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            // ...
            .headers(headers ->
                headers
                    .frameOptions(frameOptions ->
                        frameOptions.sameOrigin()
                    )
                    .httpStrictTransportSecurity(hsts ->
                        hsts.disable()
                    )
            );
    }
}

Alternatively, if you are using Spring Security XML Configuration, you can use the following:

<http>
    <!-- ... -->

    <headers>
        <frame-options policy="SAMEORIGIN" />
        <hsts disable="true"/>
    </headers>
</http>

If you do not want the defaults to be added and want explicit control over what should be used, you can disable the defaults. An example for both Java and XML based configuration is provided below:

If you are using Spring Security’s Java Configuration the following will only add Cache Control.

@EnableWebSecurity
public class WebSecurityConfig extends
WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            // ...
            .headers(headers ->
                headers
                    // do not use any default headers unless explicitly listed
                    .defaultsDisabled()
                    .cacheControl(withDefaults())
            );
    }
}

The following XML will only add Cache Control.

<http>
    <!-- ... -->

    <headers defaults-disabled="true">
        <cache-control/>
    </headers>
</http>

If necessary, you can disable all of the HTTP Security response headers with the following Java Configuration:

@EnableWebSecurity
public class WebSecurityConfig extends
WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            // ...
            .headers(headers ->
                headers.disable()
            );
    }
}

If necessary, you can disable all of the HTTP Security response headers with the following XML configuration below:

<http>
    <!-- ... -->

    <headers disabled="true" />
</http>

13.2.2 Cache Control

In the past Spring Security required you to provide your own cache control for your web application. This seemed reasonable at the time, but browser caches have evolved to include caches for secure connections as well. This means that a user may view an authenticated page, log out, and then a malicious user can use the browser history to view the cached page. To help mitigate this Spring Security has added cache control support which will insert the following headers into you response.

Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Pragma: no-cache
Expires: 0

Simply adding the <headers> element with no child elements will automatically add Cache Control and quite a few other protections. However, if you only want cache control, you can enable this feature using Spring Security’s XML namespace with the <cache-control> element and the [email protected] attribute.

<http>
    <!-- ... -->

    <headers defaults-disable="true">
        <cache-control />
    </headers>
</http>

Similarly, you can enable only cache control within Java Configuration with the following:

@EnableWebSecurity
public class WebSecurityConfig extends
WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            // ...
            .headers(headers ->
                headers
                    .defaultsDisabled()
                    .cacheControl(withDefaults())
            );
    }
}

If you actually want to cache specific responses, your application can selectively invoke HttpServletResponse.setHeader(String,String) to override the header set by Spring Security. This is useful to ensure things like CSS, JavaScript, and images are properly cached.

When using Spring Web MVC, this is typically done within your configuration. For example, the following configuration will ensure that the cache headers are set for all of your resources:

@EnableWebMvc
public class WebMvcConfiguration implements WebMvcConfigurer {

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry
            .addResourceHandler("/resources/**")
            .addResourceLocations("/resources/")
            .setCachePeriod(31556926);
    }

    // ...
}

13.2.3 Content Type Options

Historically browsers, including Internet Explorer, would try to guess the content type of a request using content sniffing. This allowed browsers to improve the user experience by guessing the content type on resources that had not specified the content type. For example, if a browser encountered a JavaScript file that did not have the content type specified, it would be able to guess the content type and then execute it.

[Note]Note

There are many additional things one should do (i.e. only display the document in a distinct domain, ensure Content-Type header is set, sanitize the document, etc) when allowing content to be uploaded. However, these measures are out of the scope of what Spring Security provides. It is also important to point out when disabling content sniffing, you must specify the content type in order for things to work properly.

The problem with content sniffing is that this allowed malicious users to use polyglots (i.e. a file that is valid as multiple content types) to execute XSS attacks. For example, some sites may allow users to submit a valid postscript document to a website and view it. A malicious user might create a postscript document that is also a valid JavaScript file and execute a XSS attack with it.

Content sniffing can be disabled by adding the following header to our response:

X-Content-Type-Options: nosniff

Just as with the cache control element, the nosniff directive is added by default when using the <headers> element with no child elements. However, if you want more control over which headers are added you can use the <content-type-options> element and the [email protected] attribute as shown below:

<http>
    <!-- ... -->

    <headers defaults-disabled="true">
        <content-type-options />
    </headers>
</http>

The X-Content-Type-Options header is added by default with Spring Security Java configuration. If you want more control over the headers, you can explicitly specify the content type options with the following:

@EnableWebSecurity
public class WebSecurityConfig extends
WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            // ...
            .headers(headers ->
                headers
                    .defaultsDisabled()
                    .contentTypeOptions(withDefaults())
            );
    }
}

13.2.4 HTTP Strict Transport Security (HSTS)

When you type in your bank’s website, do you enter mybank.example.com or do you enter https://mybank.example.com? If you omit the https protocol, you are potentially vulnerable to Man in the Middle attacks. Even if the website performs a redirect to https://mybank.example.com a malicious user could intercept the initial HTTP request and manipulate the response (i.e. redirect to https://mibank.example.com and steal their credentials).

Many users omit the https protocol and this is why HTTP Strict Transport Security (HSTS) was created. Once mybank.example.com is added as a HSTS host, a browser can know ahead of time that any request to mybank.example.com should be interpreted as https://mybank.example.com. This greatly reduces the possibility of a Man in the Middle attack occurring.

[Note]Note

In accordance with RFC6797, the HSTS header is only injected into HTTPS responses. In order for the browser to acknowledge the header, the browser must first trust the CA that signed the SSL certificate used to make the connection (not just the SSL certificate).

One way for a site to be marked as a HSTS host is to have the host preloaded into the browser. Another is to add the "Strict-Transport-Security" header to the response. For example the following would instruct the browser to treat the domain as an HSTS host for a year (there are approximately 31536000 seconds in a year):

Strict-Transport-Security: max-age=31536000 ; includeSubDomains ; preload

The optional includeSubDomains directive instructs Spring Security that subdomains (i.e. secure.mybank.example.com) should also be treated as an HSTS domain.

The optional preload directive instructs Spring Security that domain should be preloaded in browser as HSTS domain. For more details on HSTS preload please see https://hstspreload.org.

As with the other headers, Spring Security adds HSTS by default. You can customize HSTS headers with the <hsts> element as shown below:

<http>
    <!-- ... -->

    <headers>
        <hsts
            include-subdomains="true"
            max-age-seconds="31536000" preload="true" />
    </headers>
</http>

Similarly, you can enable only HSTS headers with Java Configuration:

@EnableWebSecurity
public class WebSecurityConfig extends
WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            // ...
            .headers(headers ->
                headers
                    .httpStrictTransportSecurity(hsts ->
                        hsts
                            .includeSubDomains(true)
                            .preload(true)
                            .maxAgeInSeconds(31536000)
                    )
            );
    }
}

13.2.5 HTTP Public Key Pinning (HPKP)

HTTP Public Key Pinning (HPKP) is a security feature that tells a web client to associate a specific cryptographic public key with a certain web server to prevent Man in the Middle (MITM) attacks with forged certificates.

To ensure the authenticity of a server’s public key used in TLS sessions, this public key is wrapped into a X.509 certificate which is usually signed by a certificate authority (CA). Web clients such as browsers trust a lot of these CAs, which can all create certificates for arbitrary domain names. If an attacker is able to compromise a single CA, they can perform MITM attacks on various TLS connections. HPKP can circumvent this threat for the HTTPS protocol by telling the client which public key belongs to a certain web server. HPKP is a Trust on First Use (TOFU) technique. The first time a web server tells a client via a special HTTP header which public keys belong to it, the client stores this information for a given period of time. When the client visits the server again, it expects a certificate containing a public key whose fingerprint is already known via HPKP. If the server delivers an unknown public key, the client should present a warning to the user.

[Note]Note

Because the user-agent needs to validate the pins against the SSL certificate chain, the HPKP header is only injected into HTTPS responses.

Enabling this feature for your site is as simple as returning the Public-Key-Pins HTTP header when your site is accessed over HTTPS. For example, the following would instruct the user-agent to only report pin validation failures to a given URI (via the report-uri directive) for 2 pins:

Public-Key-Pins-Report-Only: max-age=5184000 ; pin-sha256="d6qzRu9zOECb90Uez27xWltNsj0e1Md7GkYYkVoZWmM=" ; pin-sha256="E9CZ9INDbd+2eRQozYqqbQ2yXLVKB9+xcprMF+44U1g=" ; report-uri="https://example.net/pkp-report" ; includeSubDomains

A pin validation failure report is a standard JSON structure that can be captured either by the web application’s own API or by a publicly hosted HPKP reporting service, such as, REPORT-URI.

The optional includeSubDomains directive instructs the browser to also validate subdomains with the given pins.

Opposed to the other headers, Spring Security does not add HPKP by default. You can customize HPKP headers with the <hpkp> element as shown below:

<http>
    <!-- ... -->

    <headers>
        <hpkp
            include-subdomains="true"
            report-uri="https://example.net/pkp-report">
            <pins>
                    <pin algorithm="sha256">d6qzRu9zOECb90Uez27xWltNsj0e1Md7GkYYkVoZWmM=</pin>
                    <pin algorithm="sha256">E9CZ9INDbd+2eRQozYqqbQ2yXLVKB9+xcprMF+44U1g=</pin>
            </pins>
        </hpkp>
    </headers>
</http>

Similarly, you can enable HPKP headers with Java Configuration:

@EnableWebSecurity
public class WebSecurityConfig extends
WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            // ...
            .headers(headers ->
                headers
                    .httpPublicKeyPinning(hpkp ->
                        hpkp
                            .includeSubDomains(true)
                            .reportUri("https://example.net/pkp-report")
                            .addSha256Pins("d6qzRu9zOECb90Uez27xWltNsj0e1Md7GkYYkVoZWmM=", "E9CZ9INDbd+2eRQozYqqbQ2yXLVKB9+xcprMF+44U1g=")
                    )
            );
    }
}

13.2.6 X-Frame-Options

Allowing your website to be added to a frame can be a security issue. For example, using clever CSS styling users could be tricked into clicking on something that they were not intending (video demo). For example, a user that is logged into their bank might click a button that grants access to other users. This sort of attack is known as Clickjacking.

[Note]Note

Another modern approach to dealing with clickjacking is to use Section 13.2.8, “Content Security Policy (CSP)”.

There are a number ways to mitigate clickjacking attacks. For example, to protect legacy browsers from clickjacking attacks you can use frame breaking code. While not perfect, the frame breaking code is the best you can do for the legacy browsers.

A more modern approach to address clickjacking is to use X-Frame-Options header:

X-Frame-Options: DENY

The X-Frame-Options response header instructs the browser to prevent any site with this header in the response from being rendered within a frame. By default, Spring Security disables rendering within an iframe.

You can customize X-Frame-Options with the frame-options element. For example, the following will instruct Spring Security to use "X-Frame-Options: SAMEORIGIN" which allows iframes within the same domain:

<http>
    <!-- ... -->

    <headers>
        <frame-options
        policy="SAMEORIGIN" />
    </headers>
</http>

Similarly, you can customize frame options to use the same origin within Java Configuration using the following:

@EnableWebSecurity
public class WebSecurityConfig extends
WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            // ...
            .headers(headers ->
                headers
                    .frameOptions(frameOptions ->
                        frameOptions
                            .sameOrigin()
                    )
            );
    }
}

13.2.7 X-XSS-Protection

Some browsers have built in support for filtering out reflected XSS attacks. This is by no means foolproof, but does assist in XSS protection.

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 a XSS vulnerability in itself. Instead, it is best to block the content rather than attempt to fix it. To do this we can add the following header:

X-XSS-Protection: 1; mode=block

This header is included by default. However, we can customize it if we wanted. For example:

<http>
    <!-- ... -->

    <headers>
        <xss-protection block="false"/>
    </headers>
</http>

Similarly, you can customize XSS protection within Java Configuration with the following:

@EnableWebSecurity
public class WebSecurityConfig extends
WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            // ...
            .headers(headers ->
                headers
                    .xssProtection(xssProtection ->
                        xssProtection
                            .block(false)
                    )
            );
    }
}

13.2.8 Content Security Policy (CSP)

Content Security Policy (CSP) is a mechanism that web applications can leverage to mitigate content injection vulnerabilities, such as cross-site scripting (XSS). CSP is a declarative policy that provides a facility for web application authors to declare and ultimately inform the client (user-agent) about the sources from which the web application expects to load resources.

[Note]Note

Content Security Policy is not intended to solve all content injection vulnerabilities. Instead, CSP can be leveraged to help reduce the harm caused by content injection attacks. As a first line of defense, web application authors should validate their input and encode their output.

A web application may employ the use of CSP by including one of the following HTTP headers in the response:

  • Content-Security-Policy
  • Content-Security-Policy-Report-Only

Each of these headers are used as a mechanism to deliver a security policy to the client. A security policy contains a set of security policy directives (for example, script-src and object-src), each responsible for declaring the restrictions for a particular resource representation.

For example, a web application can declare that it expects to load scripts from specific, trusted sources, by including the following header in the response:

Content-Security-Policy: script-src https://trustedscripts.example.com

An attempt to load a script from another source other than what is declared in the script-src directive will be blocked by the user-agent. Additionally, if the report-uri directive is declared in the security policy, then the violation will be reported by the user-agent to the declared URL.

For example, if a web application violates the declared security policy, the following response header will instruct the user-agent to send violation reports to the URL specified in the policy’s report-uri directive.

Content-Security-Policy: script-src https://trustedscripts.example.com; report-uri /csp-report-endpoint/

Violation reports are standard JSON structures that can be captured either by the web application’s own API or by a publicly hosted CSP violation reporting service, such as, REPORT-URI.

The Content-Security-Policy-Report-Only header provides the capability for web application authors and administrators to monitor security policies, rather than enforce them. This header is typically used when experimenting and/or developing security policies for a site. When a policy is deemed effective, it can be enforced by using the Content-Security-Policy header field instead.

Given the following response header, the policy declares that scripts may be loaded from one of two possible sources.

Content-Security-Policy-Report-Only: script-src 'self' https://trustedscripts.example.com; report-uri /csp-report-endpoint/

If the site violates this policy, by attempting to load a script from evil.com, the user-agent will send a violation report to the declared URL specified by the report-uri directive, but still allow the violating resource to load nevertheless.

Configuring Content Security Policy

It’s important to note that Spring Security does not add Content Security Policy by default. The web application author must declare the security policy(s) to enforce and/or monitor for the protected resources.

For example, given the following security policy:

script-src 'self' https://trustedscripts.example.com; object-src https://trustedplugins.example.com; report-uri /csp-report-endpoint/

You can enable the CSP header using XML configuration with the <content-security-policy> element as shown below:

<http>
    <!-- ... -->

    <headers>
        <content-security-policy
            policy-directives="script-src 'self' https://trustedscripts.example.com; object-src https://trustedplugins.example.com; report-uri /csp-report-endpoint/" />
    </headers>
</http>

To enable the CSP 'report-only' header, configure the element as follows:

<http>
    <!-- ... -->

    <headers>
        <content-security-policy
            policy-directives="script-src 'self' https://trustedscripts.example.com; object-src https://trustedplugins.example.com; report-uri /csp-report-endpoint/"
            report-only="true" />
    </headers>
</http>

Similarly, you can enable the CSP header using Java configuration as shown below:

@EnableWebSecurity
public class WebSecurityConfig extends
WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            // ...
            .headers(headers ->
                headers
                    .contentSecurityPolicy(csp ->
                        csp
                            .policyDirectives("script-src 'self' https://trustedscripts.example.com; object-src https://trustedplugins.example.com; report-uri /csp-report-endpoint/")
                       )
            );
    }
}

To enable the CSP 'report-only' header, provide the following Java configuration:

@EnableWebSecurity
public class WebSecurityConfig extends
WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            // ...
            .headers(headers ->
                headers
                    .contentSecurityPolicy(csp ->
                        csp
                            .policyDirectives("script-src 'self' https://trustedscripts.example.com; object-src https://trustedplugins.example.com; report-uri /csp-report-endpoint/")
                            .reportOnly()
                    )
            );
    }
}

Additional Resources

Applying Content Security Policy to a web application is often a non-trivial undertaking. The following resources may provide further assistance in developing effective security policies for your site.

An Introduction to Content Security Policy

CSP Guide - Mozilla Developer Network

W3C Candidate Recommendation

13.2.9 Referrer Policy

Referrer Policy is a mechanism that web applications can leverage to manage the referrer field, which contains the last page the user was on.

Spring Security’s approach is to use Referrer Policy header, which provides different policies:

Referrer-Policy: same-origin

The Referrer-Policy response header instructs the browser to let the destination knows the source where the user was previously.

Configuring Referrer Policy

Spring Security doesn’t add Referrer Policy header by default.

You can enable the Referrer-Policy header using XML configuration with the <referrer-policy> element as shown below:

<http>
    <!-- ... -->

    <headers>
        <referrer-policy policy="same-origin" />
    </headers>
</http>

Similarly, you can enable the Referrer Policy header using Java configuration as shown below:

@EnableWebSecurity
public class WebSecurityConfig extends
WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            // ...
            .headers(headers ->
                headers
                    .referrerPolicy(referrerPolicy ->
                        referrerPolicy
                            .policy(ReferrerPolicy.SAME_ORIGIN)
                    )
            );
    }
}

13.2.10 Feature Policy

Feature Policy is a mechanism that allows web developers to selectively enable, disable, and modify the behavior of certain APIs and web features in the browser.

Feature-Policy: geolocation 'self'

With Feature Policy, developers can opt-in to a set of "policies" for the browser to enforce on specific features used throughout your site. These policies restrict what APIs the site can access or modify the browser’s default behavior for certain features.

Configuring Feature Policy

Spring Security doesn’t add Feature Policy header by default.

You can enable the Feature-Policy header using XML configuration with the <feature-policy> element as shown below:

<http>
    <!-- ... -->

    <headers>
        <feature-policy policy-directives="geolocation 'self'" />
    </headers>
</http>

Similarly, you can enable the Feature Policy header using Java configuration as shown below:

@EnableWebSecurity
public class WebSecurityConfig extends
WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            // ...
            .headers(headers ->
                headers
                    .featurePolicy("geolocation 'self'")
            );
    }
}

13.2.11 Clear Site Data

Clear Site Data is a mechanism by which any browser-side data - cookies, local storage, and the like - can be removed when an HTTP response contains this header:

Clear-Site-Data: "cache", "cookies", "storage", "executionContexts"

This is a nice clean-up action to perform on logout.

Configuring Clear Site Data

Spring Security doesn’t add the Clear Site Data header by default.

You can configure your application to send down this header on logout like so:

@EnableWebSecurity
public class WebSecurityConfig extends
WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            // ...
            .logout()
                .addLogoutHandler(new HeaderWriterLogoutHandler(new ClearSiteDataHeaderWriter(CACHE, COOKIES)));
    }
}
[Note]Note

It’s not recommended that you configure this header writer via the headers() directive. The reason for this is that any session state, say the JSESSIONID cookie, would be removed, effectively logging the user out.

13.2.12 Custom Headers

Spring Security has mechanisms to make it convenient to add the more common security headers to your application. However, it also provides hooks to enable adding custom headers.

Static Headers

There may be times you wish to inject custom security headers into your application that are not supported out of the box. For example, given the following custom security header:

X-Custom-Security-Header: header-value

When using the XML namespace, these headers can be added to the response using the <header> element as shown below:

<http>
    <!-- ... -->

    <headers>
        <header name="X-Custom-Security-Header" value="header-value"/>
    </headers>
</http>

Similarly, the headers could be added to the response using Java Configuration as shown in the following:

@EnableWebSecurity
public class WebSecurityConfig extends
WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            // ...
            .headers(headers ->
                headers
                    .addHeaderWriter(new StaticHeadersWriter("X-Custom-Security-Header","header-value"))
            );
    }
}

Headers Writer

When the namespace or Java configuration does not support the headers you want, you can create a custom HeadersWriter instance or even provide a custom implementation of the HeadersWriter.

Let’s take a look at an example of using an custom instance of XFrameOptionsHeaderWriter. Perhaps you want to allow framing of content for the same origin. This is easily supported by setting the policy attribute to "SAMEORIGIN", but let’s take a look at a more explicit example using the ref attribute.

<http>
    <!-- ... -->

    <headers>
        <header ref="frameOptionsWriter"/>
    </headers>
</http>
<!-- Requires the c-namespace.
See https://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/#beans-c-namespace
-->
<beans:bean id="frameOptionsWriter"
    class="org.springframework.security.web.header.writers.frameoptions.XFrameOptionsHeaderWriter"
    c:frameOptionsMode="SAMEORIGIN"/>

We could also restrict framing of content to the same origin with Java configuration:

@EnableWebSecurity
public class WebSecurityConfig extends
WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            // ...
            .headers(headers ->
                headers
                    .addHeaderWriter(new XFrameOptionsHeaderWriter(XFrameOptionsMode.SAMEORIGIN))
            );
    }
}

DelegatingRequestMatcherHeaderWriter

At times you may want to only write a header for certain requests. For example, perhaps you want to only protect your log in page from being framed. You could use the DelegatingRequestMatcherHeaderWriter to do so. When using the XML namespace configuration, this can be done with the following:

<http>
    <!-- ... -->

    <headers>
        <frame-options disabled="true"/>
        <header ref="headerWriter"/>
    </headers>
</http>

<beans:bean id="headerWriter"
    class="org.springframework.security.web.header.writers.DelegatingRequestMatcherHeaderWriter">
    <beans:constructor-arg>
        <bean class="org.springframework.security.web.util.matcher.AntPathRequestMatcher"
            c:pattern="/login"/>
    </beans:constructor-arg>
    <beans:constructor-arg>
        <beans:bean
            class="org.springframework.security.web.header.writers.frameoptions.XFrameOptionsHeaderWriter"/>
    </beans:constructor-arg>
</beans:bean>

We could also prevent framing of content to the log in page using java configuration:

@EnableWebSecurity
public class WebSecurityConfig extends
WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        RequestMatcher matcher = new AntPathRequestMatcher("/login");
        DelegatingRequestMatcherHeaderWriter headerWriter =
            new DelegatingRequestMatcherHeaderWriter(matcher,new XFrameOptionsHeaderWriter());
        http
            // ...
            .headers(headers ->
                headers
                    .frameOptions(frameOptions ->
                        frameOptions.disable()
                    )
                    .addHeaderWriter(headerWriter)
            );
    }
}

13.3 HTTPS

13.3.1 Adding HTTP/HTTPS Channel Security

If your application supports both HTTP and HTTPS, and you require that particular URLs can only be accessed over HTTPS, then this is directly supported using the requires-channel attribute on <intercept-url>:

<http>
<intercept-url pattern="/secure/**" access="ROLE_USER" requires-channel="https"/>
<intercept-url pattern="/**" access="ROLE_USER" requires-channel="any"/>
...
</http>

With this configuration in place, if a user attempts to access anything matching the "/secure/**" pattern using HTTP, they will first be redirected to an HTTPS URL [10]. The available options are "http", "https" or "any". Using the value "any" means that either HTTP or HTTPS can be used.

If your application uses non-standard ports for HTTP and/or HTTPS, you can specify a list of port mappings as follows:

<http>
...
<port-mappings>
    <port-mapping http="9080" https="9443"/>
</port-mappings>
</http>

Note that in order to be truly secure, an application should not use HTTP at all or switch between HTTP and HTTPS. It should start in HTTPS (with the user entering an HTTPS URL) and use a secure connection throughout to avoid any possibility of man-in-the-middle attacks.



[10] For more details on how channel-processing is implemented, see the Javadoc for ChannelProcessingFilter and related classes.