Cross-Site Request Forgery (CSRF)
Spring provides comprehensive support to protect against Cross-site request forgery (CSRF). In the following sections we will cover:
>What is a CSRF attack?
Protection against CSRF attacks
Recommendations about CSRF
What is a CSRF attack?
The best way to understand what is CSRF is attack, looking at a specific example.
Suppose your bank's website has a form that allows you to transfer money from the currently logged in user to another bank account. For example, a translation form might look like this:
<form method="post"
action="/transfer">
<input type="text"
name="amount"/>
<input type="text"
name="routingNumber"/>
<input type="text"
name="account"/>
<input type="submit"
value="Transfer"/>
</form>
The corresponding HTTP request might look like this:
POST /transfer HTTP/1.1
Host: bank.example.com
Cookie: JSESSIONID=randomid
Content-Type: application/x-www-form-urlencoded
amount=100.00&routingNumber=1234&account=9876
Now imagine that you logged into your bank’s website, and then, without logging out, visited the attackers’ website. The attackers' website contains an HTML page with the following form:
<form method="post"
action="https://bank.example.com/transfer">
<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 winning money, so you click on the submit button. In doing so, you unintentionally handed over $100 to the attacker. This is because, although the attacker's site does not see your cookies, the cookies associated with your bank are still sent along with the request.
The worst part is that this entire process could be automated using JavaScript. This means you didn't even have to press a button. Moreover, this could just as easily happen when visiting a bona fide site that has become a victim of XSS - attacks. How can you protect users from such attacks?
Protection against CSRF attacks
The reason why CSRF attacks are possible is that the HTTP request from the victim's site and the request from the attacker’s website are absolutely identical. This means that there is no way to reject requests coming from the attackers’ website and allow requests coming from the bank’s website. To protect against CSRF attacks, we need to make sure that there is something in the request that the attacker's site is unable to transmit so that we can distinguish between the two requests.
Spring provides two mechanisms to protect against CSRF attacks:
Synchronizer token template
Setting the SameSite attribute in a session cookie
Both security options require that safe methods be idempotent
Secure methods must be idempotent
For any of the CSRF protection options to work, the application must
ensure that "secure" HTTP methods are
idempotent. This means that requests with the HTTP method GET
, HEAD
,
OPTIONS
and TRACE
should not change the state of the application.
Synchronizer Token Template
The predominant and most comprehensive method of protecting against CSRF attacks is to use token synchronizer pattern. This solution is that each HTTP request will require that, in addition to our session cookie, a secure, randomly generated value called a CSRF token be present in the HTTP request.
Once the HTTP request is sent, the server must look up the intended CSRF token and compare it with the actual CSRF token in the HTTP request. If the values do not match, the HTTP request must be rejected.
The key to this work is that the actual CSRF token must be in a part of the HTTP request that is not automatically added by the browser. For example, requiring an actual CSRF token to be included in an HTTP parameter or HTTP header will protect against CSRF attacks. Requesting the actual CSRF token in the cookie will not work because cookies are automatically added by the browser to the HTTP request.
We can make the expected events less strict and only require the actual CSRF token for each HTTP request, which updates the application state. For this to work, our application must ensure that secure HTTP methods are idempotent. This will improve usability as we need to allow links to our site via links from external sites. Additionally, we don't need to add a random token to the HTTP GET method, as this could lead to token leakage.
Let's see how our example changes
when using the synchronizer token pattern. Let's assume that the actual CSRF token should be in an HTTP parameter
named _csrf
. The translation form in our application will look like this:
<form method="post"
action="/transfer">
<input type="hidden"
name="_csrf"
value="4bfd1575-3ad1-4d21-96c7-4ef2d9f86721"/>
<input type="text"
name="amount"/>
<input type="text"
name="routingNumber"/>
<input type="hidden"
name="account"/>
<input type="submit"
value="Transfer"/>
</form>
The form now contains hidden input with a CSRF token value. External sites will not be able to read the CSRF token, since the same origin policy ensures that the malicious site will not be able to read the response.
The corresponding HTTP request for a money transfer will look like this:
POST /transfer HTTP/1.1
Host: bank.example.com
Cookie: JSESSIONID=randomid
Content-Type: application/x-www-form-urlencoded
amount=100.00&routingNumber=1234&account= 9876&_csrf=4bfd1575-3ad1-4d21-96c7-4ef2d9f86721
You may notice that the HTTP request now contains a _csrf
with a safe random
value. The attacker's site will fail to pass the correct value for the _csrf
parameter (which must be
explicitly passed in the attacker's site), and the translation will fail when the server compares the actual CSRF
token with the expected CSRF token.
SameSite attribute
An emerging way to protect against CSRF
attacks is to specify SameSite attribute for cookies. The server may specify the SameSite
attribute when setting a cookie to indicate that the cookie should not be sent when coming from external sites.
Spring Security does not directly control the creation of session
cookies, so it does not provide support for the SameSite attribute. Spring Session provides support for the SameSite
attribute in servlet-based applications. CookieWebSessionIdResolver in the Spring Framework provides support for the SameSite
attribute in WebFlux-based applications.
An example HTTP response header with the SameSite
attribute might look like as follows:
Set-Cookie: JSESSIONID=randomid; Domain=bank.example.com; Secure; HttpOnly; SameSite=Lax
Valid values for the SameSite
attribute are:
Strict
– when this parameter is specified, any request coming from same site will include cookies. Otherwise, the cookie will not be included in the HTTP request.Lax
– if the specified cookies are sent when arriving from same site or if the request comes from top-level navigation and the method is idempotent. Otherwise, the cookie will not be added to the HTTP request.
Let's see how our example can be protected using the SameSite
attribute. A banking application can
protect against CSRF attacks by setting the SameSite
attribute in the session cookie.
By setting
the SameSite
attribute on our session cookie, the browser will continue to send the
JSESSIONID
cookie with requests coming from the banking site. However, the browser will no longer send
a JSESSIONID
cookie with a data transfer request coming from an attacker's website. Since the session
is no longer included in the transfer request coming from the attacker's site, the application will be protected
from the CSRF attack.
There are several important considerations to be aware of when using the SameSite
attribute to protect
against CSRF attacks.
Setting the SameSite
attribute to Strict
provides stronger
security, but may confuse users. Consider a user who visits a social networking site located at social.example.com. The user receives an email to email.example.org, which contains a link to the social
networking site. If a user clicks on a link, they have a reasonable expectation that they will be authenticated to
the social networking site. However, if the SameSite
attribute is set to Strict
, the
cookie will not be sent and the user will not be authenticated.
We could improve the security and usability of the CSRF attack protection
mechanism using SameSite
by implementing gh-7537.
Another obvious nuance: so that the SameSite
attribute protects users , the browser must provide
support for the SameSite
attribute. Most modern browsers support the SameSite attribute. However, older browsers still in use may not provide
such support.
For this reason, it is generally recommended to use the SameSite
attribute as
defense in depth rather than as the sole mechanism protection against CSRF attacks.
Cases for using CSRF protection
When should you use CSRF protection? We recommend using CSRF protection for any request that can be processed by a browser used by regular users. If you are creating a service that will be used exclusively by non-browser clients, then most likely you will need to disable CSRF protection.
CSRF protection and JSON format
A common question: “Do you need should I protect JSON requests executed by javascript?". Short answer: "Depends on the situation." However, you should be vigilant as there are CSRF exploits that can affect JSON requests. For example, an attacker could carry out a CSRF attack 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>
The result will be the following JSON structure
{ "amount": 100,
"routingNumber": "evilsRoutingNumber",
"account": "evilsAccountNumber",
"ignore_me": "=test"
}
If the application did not validate the Content-Type, it would be susceptible to this exploit. Depending on
the configuration, a Spring MVC application that checks Content-Type can 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 not stateful? This fact does not mean that you are perfectly protected. In fact, if the user is not required to perform any action in the web browser for a given request, the user will likely still be vulnerable to CSRF attacks.
For example, consider an application that uses instead of JSESSIONID is a custom cookie containing all the state inside it. When a CSRF attack occurs, a custom cookie will be sent along with the request in the same way as the JSESSIONID cookie from our previous example. This application will be vulnerable to CSRF attacks.
Applications that use Basic authentication are also vulnerable to CSRF attacks. The application is vulnerable because the browser will automatically include the username and password in any requests in the same way that the JSESSIONID cookie was sent in our previous example.
CSRF Considerations
When implemented protecting against CSRF attacks requires a few special considerations.
Login
To protect against login request forgery The HTTP login request must be protected from CSRF attacks. Protection against forged login requests is necessary to prevent an attacker from reading the victim's confidential information. The attack is carried out as follows:
A malicious user logs in via CSRF using the malicious user's credentials. The victim is then authenticated as the malicious user.
The malicious user tricks the victim into visiting the hacked site and entering sensitive information.
The information is tied to the attacker's user account, after which he can log in with his credentials and view the victim's confidential information.
Possible complication in securing HTTP requests to login from CSRF attacks is that the user may experience a session timeout, causing the request to be rejected. Session timeouts will surprise users who didn't expect to need a session to log in.
Logout
To protect against forged logout requests, HTTP The logout request must be protected from CSRF attacks. Protection against forged logout requests is necessary to prevent an attacker from reading the victim's confidential information. More information about the attack can be found in this blog.
A possible complication in securing HTTP logout requests from CSRF attacks is that the user may experience a session timeout, causing the request to be rejected. Session timeouts will surprise users who didn't expect to need a session to log out.
CSRF and Session Timeouts
Most often, the expected CSRF token is stored in the session. This means that once the session expires, the server will not find the expected CSRF token and will reject the HTTP request. There are several options for solving the timeout problem, each with their own disadvantages.
The best way to mitigate the effects of timeout is to use JavaScript to request a CSRF token when the form is submitted. The form is then updated with the CSRF token and submitted.
Another option is to use JavaScript, which tells the user that their session is about to expire. The user can click a button to continue and refresh the session.
Finally, the expected CSRF token can be stored in a cookie. This allows the expected CSRF token to survive the session.
One might ask why the expected CSRF token is not stored in a cookie by default. This is due to known exploits in which headers (for example, to indicate cookies) can be set by another domain. For the same reason, Ruby on Rails more does not skip CSRF checks if an X-Requested-With header is present. For details on how to perform the exploit, see this webappsec.org thread. Another disadvantage is that by removing the state (i.e. the timeout), you lose the ability to force invalidation of the token if it has been compromised.
Multiple Components (File Uploads)
Protecting multi-part requests (file uploads) from CSRF attacks leads to a chicken and egg problem. To prevent a CSRF attack, it is necessary to read the body of the HTTP request to obtain the actual CSRF token. However, reading the body means that the file will be downloaded, which in turn means that the external site will be able to download any file.
There are two options for using CSRF protection using multipart/form-data. Each option has its tradeoffs.
Placing the CSRF token in the body
Placing the CSRF token in the URL
Before you integrate Spring Security's CSRF protection with multipart file uploads, first make sure you can upload files without CSRF -protection.
Placing the CSRF token in the body
The first option is to add the actual CSRF token to the body of the request. If you put a CSRF token in the body, the body will be read before authorization occurs. This means that anyone can host temporary files on your server. However, only authorized users will be able to submit a file that will be processed by your application. In general, this is the recommended approach since downloading a temporary file should have little impact on most servers.
Adding a CSRF token to the URL
If the possibility of unauthorized users downloading temporary files is unacceptable, An alternative would be to add the expected CSRF token as a request parameter to the action attribute on the form. The disadvantage of this approach is the possibility of leaking query parameters. In general, the best way is to place sensitive data in the body or headers, which prevents it from leaking. Additional information can be found in section 15.1.3 of RFC 2616: Confidential Encoding information in the URI".
HiddenHttpMethodFilter
In some applications, a form parameter can be used to override an HTTP method. For example, the form below could
be used to treat the HTTP method as delete
rather than post
.
<form action="/process"
method="post">
<!-- ... -->
<input type="hidden"
name="_method"
value="delete"/>
</form>
The HTTP method is overridden in the filter. This filter must be installed before the Spring Security support
tools. Note that the overriding only happens in post
, so it's unlikely to cause any real problems.
However, the best practice is to place it before Spring Security filters.
Secure HTTP Response Headers
There are many Headers for HTTP responses that can be used to improve the security of web applications. This section covers the various HTTP response headers for which Spring Security provides explicit support. If necessary, Spring Security can also be configured to use custom headers.
Default Security Headers
Spring Security provides a standard set of security-related headers for HTTP responses, designed to provide secure settings for by default.
By default, Spring Security contains 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
If the default options don't suit your needs, you can easily remove, change, or add headers from these default options. For more information about each of these headers, see the relevant sections:
Cache Management
Content Type Options
HTTP Strict Transport Security
X-Frame-Options Header
X Header -XSS-Protection
Cache Control
By default, Spring Security disables caching to protect user content.
If the user is authorized to view sensitive information and then log out, we need to avoid a situation where an attacker can click the back button and view sensitive information. The following cache control headers are sent by default:
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Pragma: no-cache
Expires: 0
To provide default protection, Spring Security adds these headers by default. However, if your application provides its own cache control headers, Spring Security will not enforce its own. This allows applications to provide caching for static resources such as CSS and JavaScript.
Content Type Options
Historically, browsers, including Internet Explorer, have attempted to guess the content type of a request using content sniffing. This allowed browsers to improve the user experience by guessing the content type for resources where the content type was not specified. For example, if the browser accepts a JavaScript file that does not have a content type specified, it will be able to determine the content type and run it.
In case If content downloading is enabled, there are still many additional steps that should be taken (for example, render the document only on a specific domain, ensure that the Content-Type header is set, edit the document, etc.). However, these measures go beyond what Spring Security provides. It's also important to note that when disabling content sniffing, you must set the content type for everything to work properly.
The problem with content sniffing was that it allowed attackers to use polyglots (i.e. file that can be used as multiple content types) to conduct XSS attacks. For example, some sites may allow users to submit and view a valid postscript document to the site. A malicious user could create a postscript document that is also a valid JavaScript file, and use it to perform an XSS attack.
In Spring Security, content sniffing is disabled by default by adding the following header to HTTP responses:
X-Content-Type-Options: nosniff
HTTP Strict Transport Security (HSTS)
When you type your website bank, then enter mybank.example.com or mybank.example.com? Bypassing the https protocol creates a potential vulnerability to man-in-the-middle attacks. intermediary). Even if a site redirects to mybank.example.com, an attacker could intercept the original HTTP request and manipulate the response (e.g. redirect to mibank.example.com and steal credentials).
Many users skip the https protocol, so the HTTP Strict Transport Security (HSTS). After adding mybank.example.com as a HSTS host the browser can understand in advance that any request to mybank.example.com should be interpreted as mybank.example.com. This greatly reduces the likelihood of a man-in-the-middle attack.
According to RFC6797, the HSTS header is only embedded in HTTPS responses. In order for the browser to validate the header, it must first trust the certificate authority that signed the SSL certificate used to create the connection (not just the SSL certificate).
One way to mark a site as HSTS -host is preloading the host into the browser. The second way is to add a Strict-Transport-Security
header to the response. For example, by default, Spring Security adds the following header, which instructs the
browser to treat the domain as an HSTS host for a year (there are approximately 31,536,000 seconds in a year):
Strict-Transport-Security: max-age=31536000 ; includeSubDomains ; preload
The optional includeSubDomains
directive tells the browser that subdomains (for example,
secure.mybank.example.com) should also be considered HSTS -domains.
The optional preload
directive tells the browser that the domain should be preloaded into the
browser as an HSTS domain. More information about HSTS preloading can be found at hstspreload.org.
HTTP Public Key Pinning ( HPKP)
To enable passive operation, Spring Security still provides support for HPKP in servlet environments, but for the reasons listed above, the command security can no longer recommend the use of HPKP.
HTTP Public Key Pinning (HPKP) tells a web client what public key to use with a specific web server to prevent man-in-the-middle (MITM) attacks with forged certificates. When used correctly, HPKP can add additional layers of protection against compromised certificates. However, due to the complexity of HPKP, many experts no longer recommend using it, and Chrome has even stopped supporting it.
For more information on why HPKP is no longer recommended, read the articles "Is HTTP Public Key Pinning Dead?" and "I'm giving up on HPKP".
X-Frame-Options header
Permission to add your site to the frame can become a security issue. For example, clever CSS styling can trick users into clicking something they didn't intend to click. For example, a user logged into their online banking can click a button that grants access to other users. This type of attack is known as clickjacking.
Another modern approach to combating clickjacking is the use of the Content Security Policy (CSP) specification.
There are ways to counter clickjacking attacks. For example, to protect legacy browsers from clickjacking attacks, you can use frame break code. While not perfect, the frame disabling code is the best you can do for legacy browsers.
A more modern approach to solving the clickjacking problem is to use the X-Frame-Options. By default, Spring Security prevents pages from being rendered in an inline frame (iframe) when using the following header:
X-Frame-Options: DENY
X-XSS-Protection Header
Some browsers have built-in support for filtering reflected XSS attacks . This is by no means foolproof, but it does help protect against XSS attacks.
Filtering is most often enabled by default, so adding a header usually just ensures it is enabled and tells the browser what to do when detecting an XSS attack. For example, a filter might try to change the content in the least invasive way possible to render everything it needs. Sometimes such a replacement can itself become an XSS vulnerability. Instead, it is better to block the content rather than try to fix it. By default, Spring Security blocks content using the following header:
X-XSS-Protection: 1; mode=block
Content Security Policy (CSP) Specification
Content Security Policy (CSP) is a mechanism that web applications can use to reduce the risk of content injection vulnerabilities such as cross-site scripting (XSS). CSP is a declarative policy that gives web application authors the ability to declare and ultimately communicate to the client (user agent) the sources from which the web application expects resources to be loaded.
Content Security Policy will not solve all problems associated with content injection vulnerabilities. Instead, CSP can be used to reduce the harm caused by content injection attacks. As a first line of defense, web application authors must validate their input and encode output.
A web application can use CSP if it adds one of the following HTTP headers to the response:
Content-Security-Policy
Content-Security-Policy-Report-Only
Each of these headers is used as a mechanism to enforce security policy for the client. A security policy contains a set of security policy directives, each of which is responsible for declaring restrictions on a particular representation of a resource.
For example, a web application might declare that it expects scripts to be loaded from certain, trusted sources if you add in response the following title:
Content-Security-Policy: script-src https://trustedscripts.example.com
An attempt to load a script from a source other than that declared in the script-src
directive will be blocked by the user agent. In addition, if the report-uri directive is
declared in the security policy, then the user agent will report the violation to the advertised URL.
For example, if a web application violates the advertised security policy, the following response header will
tell the user agent to send violation reports to the URL specified in the report-uri
policy
directive.
Content-Security-Policy: script-src https://trustedscripts.example.com ; report-uri /csp-report-endpoint/
Abuse Reports are standard JSON structures that can be retrieved either through the web application's native API or through a publicly hosted CSP abuse reporting service, e.g.,report-uri.com/.
Heading Content-Security-Policy-Report-Only
gives web application authors and administrators the
ability to monitor security policies rather than enforce them. This header is typically used when conducting
experiments and/or developing security policies for a site. If the policy is considered effective, it can be
applied using the Content-Security-Policy
header field instead.
Given the following response header, the policy declares that scripts can 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 a site violates this policy by attempting to download a script from evil.com, the user agent will send a violation report to the URL specified by the report-uri directive, but will still allow the offending resource to load.
Applying the Content Security Policy specification to a web application is often not such a simple task. The following resources may be of further assistance in developing an effective security policy for your site.
Introduction to Content Security Policy
CSP Guide - Mozilla Developer Network
Referrer Policy Specification
Referrer Policy is a mechanism that web applications can use to control the referrer field containing the last page the user was on.
The Spring Security approach is to use the Referrer Policy, which provides various policies:
Referrer-Policy: same-origin
The Referrer-Policy response header instructs the browser to tell the recipient the source where the user was previously located.
Feature Policy Specification
Feature Policy is a mechanism that allows web developers to selectively enable, disable, and change the logic of certain APIs and web features in the browser .
Feature-Policy: geolocation 'self'
With Feature Policy, developers can choose a set of " policies" that your browser will apply to certain features used on your site. These policies restrict a site's access to APIs or change the default browser behavior for certain features.
Permissions Policy Specification
Permissions Policy is a mechanism that allows web developers to selectively enable, disable, and change the logic of certain APIs and web functions in the browser.
Permissions-Policy: geolocation=(self)
With Permissions Policy, developers can choose a set of “policies” that the browser will apply to certain features used on your site. These policies restrict a site's access to APIs or change the default browser behavior for certain features.
Clear Site Data Header
Clear Site Data is a mechanism by which any data - cookies, local storage and the like - can be deleted on the browser side if HTTP the response will contain this header:
Clear-Site-Data: "cache", "cookies", "storage", "executionContexts"
This is an effective cleanup measure performed when exiting systems.
Cross-Origin Policies Specification
Spring Security provides support for some important headers of the Cross-Origin Policies specification. These headers are:
Cross-Origin-Opener-Policy
header (COOP) allows the top-level document to break the connection
between its window and any others in the viewing context group (for example, between a popup window and its
opener), preventing direct access to the DOM between them.
Activation Cross-Origin-Embedder-Policy
(COEP) prevents loading into a document any resources from
different origins that do not explicitly grant the document permission to load.
Header Cross-Origin-Resource-Policy
(CORP) allows you to control the set of origins that have the
right to add a resource. This is a strong defense against Spectre
attacks because it allows browsers to block a given response before it reaches the attacker's process.
Custom headers
Spring Security contains mechanisms that allow you to conveniently add the most common security headers to your application. However, it also provides hooks for adding custom headers.
HTTP
All communication is over HTTP, including static resources should be protected using TLS.
As a framework, Spring Security does not handle HTTP connections and therefore does not directly support HTTPS. However, it does provide a number of features that help you use HTTPS.
Redirecting to HTTPS
If the client is using HTTP, you can add a redirect to HTTPS to your Spring Security configuration, just like in a servlet environment. as well as in the WebFlux environment.
Strict Transport Security
Spring Security provides support for the Strict Transport Security mechanism and activates it by default.
Proxy server configuration
When using a proxy server, it is important to ensure that the application has been configured correctly. For example, many applications will contain a load balancer that responds to a example.com/ request by forwarding it to the application server at 192.168.1:8080. Without proper configuration, the application server will not be aware of the existence of the load balancer and will process the request as if the client had requested 192.168.1:8080.
This can be fixed by using RFC 7239 to specify the use load balancer. To ensure application compatibility, you must configure the application server to be compatible with X-Forwarded headers. For example, Tomcat uses RemoteIpValve, and Jetty - ForwardedRequestCustomizer. Additionally, Spring users can use the ForwardedHeaderFilter.
Spring Boot users can use the server.use-forward-headers
property to configure the application.
GO TO FULL VERSION