123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389 |
- // FIXME: Add links to Servlet and WebFlux support
- [[csrf]]
- = Cross Site Request Forgery (CSRF)
- Spring provides comprehensive support for protecting against https://en.wikipedia.org/wiki/Cross-site_request_forgery[Cross Site Request Forgery (CSRF)] attacks.
- In the following sections, we explore:
- * <<csrf-explained>>
- * <<csrf-protection>>
- * <<csrf-considerations>>
- // FIXME: Add WebFlux csrf documentation (the link below is broken)
- [NOTE]
- ====
- This portion of the documentation discusses the general topic of CSRF protection.
- See the relevant sections for specific information on CSRF protection for xref:servlet/exploits/csrf.adoc#servlet-csrf[servlet] and xref:reactive/exploits/csrf.adoc#webflux-csrf[WebFlux] based applications.
- ====
- [[csrf-explained]]
- == What is a CSRF Attack?
- The best way to understand a CSRF attack is by taking a look at a concrete example.
- 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 transfer form might look like:
- .Transfer form
- [source,html]
- ----
- <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:
- .Transfer HTTP request
- [source]
- ----
- 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 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:
- .Evil transfer form
- [source,html]
- ----
- <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 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.
- Worse yet, this whole process could have been automated by using JavaScript.
- This means you did not even need to click on the button.
- Furthermore, it could just as easily happen when visiting an honest site that is a victim of a https://www.owasp.org/index.php/Cross-site_Scripting_(XSS)[XSS attack].
- So how do we protect our users from such attacks?
- [[csrf-protection]]
- == Protecting Against CSRF Attacks
- The reason that a CSRF attack is possible is that the HTTP request from the victim's website and the request from the attacker's website are exactly the same.
- This means there is no way to reject requests coming from the evil website and allow only 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 so we can differentiate the two requests.
- Spring provides two mechanisms to protect against CSRF attacks:
- * The <<Synchronizer Token Pattern>>
- * Specifying the <<SameSite Attribute>> on your session cookie
- [NOTE]
- ====
- Both protections require that <<csrf-protection-read-only,Safe Methods be Read-only>>.
- ====
- [[csrf-protection-read-only]]
- === Safe Methods Must be Read-only
- For <<csrf-protection,either protection>> against CSRF to work, the application must ensure that https://tools.ietf.org/html/rfc7231#section-4.2.1["safe" HTTP methods are read-only].
- This means that requests with the HTTP `GET`, `HEAD`, `OPTIONS`, and `TRACE` methods should not change the state of the application.
- [[csrf-protection-stp]]
- === Synchronizer Token Pattern
- The predominant and most comprehensive way to protect against CSRF attacks is to use the https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html#synchronizer-token-pattern[Synchronizer Token Pattern].
- This solution is to ensure that each HTTP request requires, in addition to our session cookie, a secure random generated value called a CSRF token be present in the HTTP request.
- When an HTTP request is submitted, the server must look up the expected CSRF token and compare it against the actual CSRF token in the HTTP request.
- If the values do not match, the HTTP request should be rejected.
- The key to this working is that the actual CSRF token should be in a part of the HTTP request that is not automatically included by the browser.
- For example, requiring the actual CSRF token in an HTTP parameter or an HTTP header will protect against CSRF attacks.
- Requiring the actual CSRF token in a cookie does not work because cookies are automatically included in the HTTP request by the browser.
- We can relax the expectations to require only the actual CSRF token for each HTTP request that updates the state of the application.
- For that to work, our application must ensure that <<csrf-protection-read-only,safe HTTP methods are read-only>>.
- This improves usability, since we want to allow linking to our website from external sites.
- Additionally, we do not want to include the random token in HTTP GET, as this can cause the tokens to be leaked.
- Consider how <<csrf-explained,our example>> would change when we use the Synchronizer Token Pattern.
- Assume that the actual CSRF token is required to be in an HTTP parameter named `_csrf`.
- Our application's transfer form would look like:
- .Synchronizer Token Form
- [source,html]
- ----
- <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 a hidden input with the value of the CSRF token.
- External sites cannot read the CSRF token since the same origin policy ensures the evil site cannot read the response.
- The corresponding HTTP request to transfer money would look like this:
- .Synchronizer Token request
- [source]
- ----
- 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 will notice that the HTTP request now contains the `_csrf` parameter with a secure random value.
- The evil website will not be able to provide 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 CSRF token to the expected CSRF token.
- [[csrf-protection-ssa]]
- === SameSite Attribute
- An emerging way to protect against <<csrf,CSRF Attacks>> is to specify the https://tools.ietf.org/html/draft-west-first-party-cookies[SameSite Attribute] on cookies.
- A server can specify the `SameSite` attribute when setting a cookie to indicate that the cookie should not be sent when coming from external sites.
- [NOTE]
- ====
- Spring Security does not directly control the creation of the session cookie, so it does not provide support for the SameSite attribute.
- https://spring.io/projects/spring-session[Spring Session] provides support for the `SameSite` attribute in servlet-based applications.
- Spring Framework's https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/server/session/CookieWebSessionIdResolver.html[`CookieWebSessionIdResolver`] provides out of the box support for the `SameSite` attribute in WebFlux-based applications.
- ====
- An example, of an HTTP response header with the `SameSite` attribute might look like:
- .SameSite HTTP response
- [source]
- ----
- Set-Cookie: JSESSIONID=randomid; Domain=bank.example.com; Secure; HttpOnly; SameSite=Lax
- ----
- Valid values for the `SameSite` attribute are:
- * `Strict`: When specified, any request coming from the https://tools.ietf.org/html/draft-west-first-party-cookies-07#section-2.1[same-site] includes the cookie.
- Otherwise, the cookie is not included in the HTTP request.
- * `Lax`: When specified, cookies are sent when coming from the https://tools.ietf.org/html/draft-west-first-party-cookies-07#section-2.1[same-site] or when the request comes from top-level navigations and the <<Safe Methods Must be Read-only,method is read-only>>.
- Otherwise, the cookie is not included in the HTTP request.
- Consider how <<csrf-explained,our example>> could be protected using the `SameSite` attribute.
- The bank application can protect against CSRF by specifying the `SameSite` attribute on the session cookie.
- With the `SameSite` attribute set on our session cookie, the browser continues to send the `JSESSIONID` cookie with requests coming from the banking website.
- However, the browser no longer sends the `JSESSIONID` cookie with a transfer request coming from the evil website.
- Since the session is no longer present in the transfer request coming from the evil website, the application is protected from the CSRF attack.
- There are some important https://tools.ietf.org/html/draft-west-first-party-cookies-07#section-5[considerations] to be aware of when using `SameSite` attribute to protect against CSRF attacks.
- Setting the `SameSite` attribute to `Strict` provides a stronger defense but can confuse users.
- Consider a user who stays logged into a social media site hosted at https://social.example.com.
- The user receives an email at https://email.example.org that includes a link to the social media site.
- If the user clicks on the link, they would rightfully expect to be authenticated to the social media site.
- However, if the `SameSite` attribute is `Strict`, the cookie would not be sent and so the user would not be authenticated.
- Another obvious consideration is that, in order for the `SameSite` attribute to protect users, the browser must support the `SameSite` attribute.
- Most modern browsers do https://developer.mozilla.org/en-US/docs/Web/HTTP/headers/Set-Cookie#Browser_compatibility[support the SameSite attribute].
- However, older browsers that are still in use may not.
- For this reason, we generally recommend using the `SameSite` attribute as a defense in depth rather than the sole protection against CSRF attacks.
- [[csrf-when]]
- == 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 creating a service that is used only by non-browser clients, you likely want to disable CSRF protection.
- [[csrf-when-json]]
- === 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 http://blog.opensecurityresearch.com/2012/02/json-csrf-with-parameter-padding.html[CSRF with JSON by using the following form]:
- .CSRF with JSON form
- [source,html]
- ----
- <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 produces the following JSON structure
- .CSRF with JSON request
- [source,javascript]
- ----
- { "amount": 100,
- "routingNumber": "evilsRoutingNumber",
- "account": "evilsAccountNumber",
- "ignore_me": "=test"
- }
- ----
- If an application were not validating the `Content-Type` header, 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 follows:
- .CSRF with JSON Spring MVC form
- [source,html]
- ----
- <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-when-stateless]]
- === CSRF and Stateless Browser Applications
- What if my application is stateless?
- That does not 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 that 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 is sent with the request in the same manner that the JSESSIONID cookie was sent in our previous example.
- This application is vulnerable to CSRF attacks.
- Applications that use basic authentication are also vulnerable to CSRF attacks.
- The application is vulnerable since the browser automatically includes the username and password in any requests in the same manner that the JSESSIONID cookie was sent in our previous example.
- [[csrf-considerations]]
- == CSRF Considerations
- There are a few special considerations to consider when implementing protection against CSRF attacks.
- // FIXME: Document rotating the CSRF token at log in to avoid a fixation attack
- [[csrf-considerations-login]]
- === Logging In
- To protect against https://en.wikipedia.org/wiki/Cross-site_request_forgery#Forging_login_requests[forging login requests], the login HTTP request should be protected against CSRF attacks.
- Protecting against forging login requests is necessary so that a malicious user cannot read a victim's sensitive information.
- The attack is performed as follows:
- . A malicious user performs a CSRF login with the malicious user's credentials.
- The victim is now authenticated as the malicious user.
- . The malicious user then tricks the victim into visiting the compromised website and entering sensitive information.
- . The information is associated to the malicious user's account so the malicious user can log in with their own credentials and view the victim's sensitive information.
- A possible complication to ensuring login HTTP requests are protected against CSRF attacks is that the user might experience a session timeout that causes the request to be rejected.
- A session timeout is surprising to users who do not expect to need to have a session to log in.
- For more information refer to <<csrf-considerations-timeouts>>.
- [[csrf-considerations-logout]]
- === Logging Out
- To protect against forging logout requests, the logout HTTP request should be protected against CSRF attacks.
- Protecting against forging logout requests is necessary so that a malicious user cannot read a victim's sensitive information.
- For details on the attack, see https://labs.detectify.com/2017/03/15/loginlogout-csrf-time-to-reconsider/[this blog post].
- A possible complication to ensuring logout HTTP requests are protected against CSRF attacks is that the user might experience a session timeout that causes the request to be rejected.
- A session timeout is surprising to users who do not expect to have a session to log out.
- For more information, see <<csrf-considerations-timeouts>>.
- [[csrf-considerations-timeouts]]
- === CSRF and Session Timeouts
- More often than not, the expected CSRF token is stored in the session.
- This means that, as soon as the session expires, the server does not find an expected CSRF token and rejects the HTTP request.
- There are a number of options (each of which come with trade offs) to solve timeouts:
- * The best way to mitigate the timeout is by using JavaScript to request a CSRF token on form submission.
- The form is then updated with the CSRF token and submitted.
- * Another option 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.
- * Finally, the expected CSRF token could be stored in a cookie.
- This lets the expected CSRF token outlive the session.
- +
- One might ask why the expected CSRF token is not stored in a cookie by default.
- This is because there are known exploits in which headers (for example, to specify the cookies) can be set by another domain.
- This is the same reason Ruby on Rails https://rubyonrails.org/2011/2/8/csrf-protection-bypass-in-ruby-on-rails[no longer skips a CSRF checks when the header X-Requested-With is present].
- See https://web.archive.org/web/20210221120355/https://lists.webappsec.org/pipermail/websecurity_lists.webappsec.org/2011-February/007533.html[this webappsec.org thread] for details on how to perform the exploit.
- Another disadvantage is that by removing the state (that is, the timeout), you lose the ability to forcibly invalidate the token if it is compromised.
- // FIXME: Document timeout with lengthy form expire. We do not want to automatically replay that request because it can lead to an exploit.
- [[csrf-considerations-multipart]]
- === Multipart (file upload)
- Protecting multipart requests (file uploads) from CSRF attacks causes a https://en.wikipedia.org/wiki/Chicken_or_the_egg[chicken or the egg] problem.
- To prevent a CSRF attack from occurring, the body of the HTTP request must be read to obtain the actual CSRF token.
- However, reading the body means that the file is uploaded, which means an external site can upload a file.
- There are two options to using CSRF protection with multipart/form-data:
- * <<csrf-considerations-multipart-body,Place CSRF Token in the Body>>
- * <<csrf-considerations-multipart-url,Place CSRF Token in the URL>>
- Each option has its trade-offs.
- [NOTE]
- ====
- Before you integrate Spring Security's CSRF protection with multipart file upload, you should first ensure that you can upload without the CSRF protection.
- More information about using multipart forms with Spring, see the https://docs.spring.io/spring/docs/5.2.x/spring-framework-reference/web.html#mvc-multipart[1.1.11. Multipart Resolver] section of the Spring reference and the https://docs.spring.io/spring/docs/5.2.x/javadoc-api/org/springframework/web/multipart/support/MultipartFilter.html[`MultipartFilter` Javadoc].
- ====
- [[csrf-considerations-multipart-body]]
- ==== Place CSRF Token in the Body
- The first option is to include the actual CSRF token in the body of the request.
- By placing the CSRF token in the body, the body is read before authorization is performed.
- This means that anyone can place temporary files on your server.
- However, only authorized users can submit a file that is processed by your application.
- In general, this is the recommended approach, because the temporary file upload should have a negligible impact on most servers.
- [[csrf-considerations-multipart-url]]
- ==== Include CSRF Token in URL
- If letting unauthorized users upload temporary files is not acceptable, an alternative is to include the expected CSRF token as a query parameter in the action attribute of the form.
- The disadvantage to this approach is that query parameters can be leaked.
- More generally, it is considered best practice to place sensitive data within the body or headers to ensure it is not leaked.
- You can find additional information in https://www.w3.org/Protocols/rfc2616/rfc2616-sec15.html#sec15.1.3[RFC 2616 Section 15.1.3 Encoding Sensitive Information in URI's].
- [[csrf-considerations-override-method]]
- ==== HiddenHttpMethodFilter
- Some applications can use a form parameter to override the HTTP method.
- For example, the following form can treat the HTTP method as a `delete` rather than a `post`.
- .CSRF Hidden HTTP Method Form
- [source,html]
- ----
- <form action="/process"
- method="post">
- <!-- ... -->
- <input type="hidden"
- name="_method"
- value="delete"/>
- </form>
- ----
- Overriding the HTTP method occurs in a filter.
- That filter must be placed before Spring Security's support.
- Note that overriding happens only on a `post`, so this is actually unlikely to cause any real problems.
- However, it is still best practice to ensure that it is placed before Spring Security's filters.
|