| 
 | 1 | +<chapter xmlns="http://docbook.org/ns/docbook" version="5.0" xml:id="csrf"  | 
 | 2 | + xmlns:xlink="http://www.w3.org/1999/xlink">  | 
 | 3 | + <info>  | 
 | 4 | + <title>Cross Site Request Forgery (CSRF)</title>  | 
 | 5 | + </info>  | 
 | 6 | + <para>This section discusses Spring Security's <link xlink:href="http://en.wikipedia.org/wiki/Cross-site_request_forgery">  | 
 | 7 | + Cross Site Request Forgery (CSRF)</link> support.</para>  | 
 | 8 | + <section>  | 
 | 9 | + <title>CSRF Attacks</title>  | 
 | 10 | + <para>Before we discuss how Spring Security can protect applications from CSRF attacks, we will explain what a CSRF  | 
 | 11 | + attack is. Let's take a look at a concrete example to get a better understanding.</para>  | 
 | 12 | + <para>Assume that your bank's website provides a form that allows transferring money from the currently logged in user  | 
 | 13 | + to another bank account. For example, the HTTP request might look like:</para>  | 
 | 14 | + <programlisting><![CDATA[POST /transfer HTTP/1.1  | 
 | 15 | +Host: bank.example.com  | 
 | 16 | +Cookie: JSESSIONID=randomid; Domain=bank.example.com; Secure; HttpOnly  | 
 | 17 | +Content-Type: application/x-www-form-urlencoded  | 
 | 18 | +
  | 
 | 19 | +amount=100.00&routingNumber=1234&account=9876  | 
 | 20 | +]]></programlisting>  | 
 | 21 | + <para>Now pretend you authenticate to your bank's website and then, without logging out, visit an evil website. The evil  | 
 | 22 | + website contains an HTML page with the following form:</para>  | 
 | 23 | + <programlisting language="xml"><![CDATA[<form action="https://bank.example.com/transfer" method="post">  | 
 | 24 | + <input type="hidden"  | 
 | 25 | + name="amount"  | 
 | 26 | + value="100.00"/>  | 
 | 27 | + <input type="hidden"  | 
 | 28 | + name="routingNumber"  | 
 | 29 | + value="evilsRoutingNumber"/>  | 
 | 30 | + <input type="hidden"  | 
 | 31 | + name="account"  | 
 | 32 | + value="evilsAccountNumber"/>  | 
 | 33 | + <input type="submit"  | 
 | 34 | + value="Win Money!'/>  | 
 | 35 | +</form>]]></programlisting>  | 
 | 36 | + <para>You like to win money, so you click on the submit button. In the process, you have unintentionally transferred $100 to  | 
 | 37 | + a malicious user. This happens because, while the evil website cannot see your cookies, the cookies associated with your  | 
 | 38 | + bank are still sent along with the request.</para>  | 
 | 39 | + <para>Worst yet, this whole process could have been automated using JavaScript. This means you didn't even need to click on the  | 
 | 40 | + button. So how do we protect ourselves from such attacks?</para>  | 
 | 41 | + </section>  | 
 | 42 | + <section>  | 
 | 43 | + <title>Synchronizer Token Pattern</title>  | 
 | 44 | + <para>The issue is that the HTTP request from the bank's website and the request from the evil website are exactly the same. This  | 
 | 45 | + means there is no way to reject requests coming from the evil website and allow requests coming from the bank's website. To  | 
 | 46 | + protect against CSRF attacks we need to ensure there is something in the request that the evil site is unable to provide.</para>  | 
 | 47 | + <para>One solution is to use the  | 
 | 48 | + <link xlink:href="https://www.owasp.org/index.php/Cross-Site_Request_Forgery_(CSRF)_Prevention_Cheat_Sheet#General_Recommendation:_Synchronizer_Token_Pattern">Synchronizer  | 
 | 49 | + Token Pattern</link>. This solution is to ensure that each request requires, in addition to our session cookie, a randomly  | 
 | 50 | + generated token as an HTTP parameter. When a request is submitted, the server must look up the expected value for the parameter  | 
 | 51 | + and compare it against the actual value in the request. If the values do not match, the request should fail.</para>  | 
 | 52 | + <para>We can relax the expectations to only require the token for each HTTP request that updates state. This can be safely done  | 
 | 53 | + since the same origin policy ensures the evil site cannot read the response. Additionally, we do not want to include the random  | 
 | 54 | + token in HTTP GET as this can cause the tokens to be leaked.</para>  | 
 | 55 | + <para>Let's take a look at how our example would change. Assume the randomly generated token is present in an HTTP parameter named  | 
 | 56 | + _csrf. For example, the request to transfer money would look like this:</para>  | 
 | 57 | + <programlisting><![CDATA[POST /transfer HTTP/1.1  | 
 | 58 | +Host: bank.example.com  | 
 | 59 | +Cookie: JSESSIONID=randomid; Domain=bank.example.com; Secure; HttpOnly  | 
 | 60 | +Content-Type: application/x-www-form-urlencoded  | 
 | 61 | +
  | 
 | 62 | +amount=100.00&routingNumber=1234&account=9876&_csrf=<secure-random>  | 
 | 63 | +]]></programlisting>  | 
 | 64 | + <para>You will notice that we added the _csrf parameter with a random value. Now the evil website will not be able to guess the  | 
 | 65 | + correct value for the _csrf parameter (which must be explicitly provided on the evil website) and the transfer will fail when the  | 
 | 66 | + server compares the actual token to the expected token.</para>  | 
 | 67 | + </section>  | 
 | 68 | + <section>  | 
 | 69 | + <title>Using Spring Security CSRF Support</title>  | 
 | 70 | + <para>So what are the steps necessary to use Spring Security's to protect our site against CSRF attacks? The steps to using Spring  | 
 | 71 | + Security's CSRF protection are outlined below:</para>  | 
 | 72 | + <orderedlist inheritnum="ignore" continuation="restarts">  | 
 | 73 | + <listitem>  | 
 | 74 | + <para><link xlink:href="#csrf-use-proper-verbs">Use proper HTTP verbs</link></para>  | 
 | 75 | + </listitem>  | 
 | 76 | + <listitem>  | 
 | 77 | + <para><link xlink:href="#csrf-configure">Configure CSRF Protection</link></para>  | 
 | 78 | + </listitem>  | 
 | 79 | + <listitem>  | 
 | 80 | + <para><link xlink:href="#csrf-include-csrf-token">Include the CSRF Token</link></para>  | 
 | 81 | + </listitem>  | 
 | 82 | + </orderedlist>  | 
 | 83 | + <section xml:id="csrf-use-proper-verbs">  | 
 | 84 | + <title>Use proper HTTP verbs</title>  | 
 | 85 | + <para>The first step to protecting against CSRF attacks is to ensure your website uses proper HTTP verbs. Specifically, before Spring  | 
 | 86 | + 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  | 
 | 87 | + that modifies state. This is not a limitation of Spring Security's support, but instead a general requirement for proper CSRF prevention.</para>  | 
 | 88 | + </section>  | 
 | 89 | + <section xml:id="csrf-configure">  | 
 | 90 | + <title>Configure CSRF Protection</title>  | 
 | 91 | + <para>The next step is to include Spring Security's CSRF protection within your application. If you are using the XML configuration, this can be done  | 
 | 92 | + using the <link xlink:href="#nsa-csrf"><csrf /></link> element:</para>  | 
 | 93 | + <programlisting language="xml"><![CDATA[<http ...>  | 
 | 94 | + ...  | 
 | 95 | + <csrf />  | 
 | 96 | +</http>  | 
 | 97 | +]]></programlisting>  | 
 | 98 | + <para>CSRF protection is enabled by default with Java configuration. If you would like to disable CSRF, the corresponding Java configuration can be  | 
 | 99 | + seen below:</para>  | 
 | 100 | + <programlisting language="java"><![CDATA[@EnableWebSecurity  | 
 | 101 | +@Configuration  | 
 | 102 | +public class WebSecurityConfig extends  | 
 | 103 | + WebSecurityConfigurerAdapter {  | 
 | 104 | +
  | 
 | 105 | + @Override  | 
 | 106 | + protected void configure(HttpSecurity http) throws Exception {  | 
 | 107 | + http  | 
 | 108 | + .csrf().disable()  | 
 | 109 | + ...;  | 
 | 110 | + }  | 
 | 111 | +}]]></programlisting>  | 
 | 112 | + </section>  | 
 | 113 | + <section xml:id="csrf-include-csrf-token">  | 
 | 114 | + <title>Include the CSRF Token</title>  | 
 | 115 | + <section xml:id="csrf-include-csrf-token-form">  | 
 | 116 | + <title>Form Submissions</title>  | 
 | 117 | + <para>The last step is to ensure that you include the CSRF token in all PATCH, POST, PUT, and DELETE methods. This can be done using  | 
 | 118 | + the _csrf request attribute to obtain the current CsrfToken. An example of doing this with a JSP is shown below:</para>  | 
 | 119 | + <programlisting language="xml"><![CDATA[<c:url var="logoutUrl" value="/logout"/>  | 
 | 120 | +<form action="${logoutUrl}"  | 
 | 121 | + method="post">  | 
 | 122 | + <input type="submit"  | 
 | 123 | + value="Log out" />  | 
 | 124 | + <input type="hidden"  | 
 | 125 | + name="${_csrf.parameterName}"  | 
 | 126 | + value="${_csrf.token}"/>  | 
 | 127 | +</form>]]></programlisting>  | 
 | 128 | + <note>  | 
 | 129 | + <para>If you are using Spring MVC <form:form> tag, the <interfacename>CsrfToken</interfacename> is automatically included for you using the CsrfRequestDataValueProcessor.</para>  | 
 | 130 | + </note>  | 
 | 131 | + </section>  | 
 | 132 | + <section xml:id="csrf-include-csrf-token-ajax">  | 
 | 133 | + <title>Ajax Requests</title>  | 
 | 134 | + <para>If you 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.  | 
 | 135 | + A typical pattern would be to include the CSRF token within your meta tags. An example with a JSP is shown below:</para>  | 
 | 136 | + <programlisting language="xml"><![CDATA[<html>  | 
 | 137 | + <head>  | 
 | 138 | + <meta name="_csrf" content="${_csrf.token}"/>  | 
 | 139 | + <!-- default header name is X-CSRF-TOKEN -->  | 
 | 140 | + <meta name="_csrf_header" content="${_csrf.headerName}"/>  | 
 | 141 | + ...  | 
 | 142 | + </head>  | 
 | 143 | + ...]]></programlisting>  | 
 | 144 | + <para>You can then include the token within all your AJAX requests. If you were using JQuery, this could be done with the following:</para>  | 
 | 145 | + <programlisting language="javascript"><![CDATA[$(document).ajaxSend(function(e, xhr, options) {  | 
 | 146 | + var token = $("meta[name='_csrf']").attr("content");  | 
 | 147 | + var header = $("meta[name='_csrf_header']").attr("content");  | 
 | 148 | + xhr.setRequestHeader(header, token);  | 
 | 149 | +});]]></programlisting>  | 
 | 150 | + </section>  | 
 | 151 | + </section>  | 
 | 152 | + </section>  | 
 | 153 | + <section>  | 
 | 154 | + <title>CSRF Caveats</title>  | 
 | 155 | + <para>There are a few caveats when implementing CSRF.</para>  | 
 | 156 | + <section>  | 
 | 157 | + <title>Timeouts</title>  | 
 | 158 | + <para>One issue is that the expected CSRF token is stored in the HttpSession, so as soon as the HttpSession expires your configured  | 
 | 159 | + <interfacename>AccessDeniedHandler</interfacename> will receive a InvalidCsrfTokenException. If you are using the default  | 
 | 160 | + <interfacename>AccessDeniedHandler</interfacename>, the browser will get an HTTP 403 and display a poor error message.</para>  | 
 | 161 | + <note>  | 
 | 162 | + <para>One might ask why the <interfacename>CsrfToken</interfacename> isn't stored in a cookie. This is because there are known exploits in which headers  | 
 | 163 | + (i.e. specify the cookies) can be set by another domain. Another disadvantage is that by removing the state (i.e. the timeout) you lose the ability  | 
 | 164 | + to forcibly terminate the token if something got compromised.</para>  | 
 | 165 | + </note>  | 
 | 166 | + <para>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.  | 
 | 167 | + The user can click a button to continue and refresh the session.</para>  | 
 | 168 | + <para>Alternatively, specifying a custom <interfacename>AccessDeniedHandler</interfacename> allows you to process the <classname>InvalidCsrfTokenException</classname>  | 
 | 169 | + anyway you like. For an example of how to customize the <interfacename>AccessDeniedHandler</interfacename> refer to the provided links for both xml and Java  | 
 | 170 | + configuration.</para>  | 
 | 171 | + </section>  | 
 | 172 | + <section>  | 
 | 173 | + <title>Logging In</title>  | 
 | 174 | + <para>In order to protect against forging log in requests the log in form should be protected against CSRF attacks too. Since the <interfacename>CsrfToken</interfacename> is stored in  | 
 | 175 | + HttpSession, this means an HttpSession will be created as soon as <interfacename>CsrfToken</interfacename> token attribute is accessed. While this sounds bad in  | 
 | 176 | + 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  | 
 | 177 | + compromised. Practically speaking, the CSRF token is quite small in size and should have a negligible impact on our architecture.</para>  | 
 | 178 | + </section>  | 
 | 179 | + <section>  | 
 | 180 | + <title>Logging Out</title>  | 
 | 181 | + <para>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  | 
 | 182 | + log out your users.</para>  | 
 | 183 | + <para>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  | 
 | 184 | + 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.</para>  | 
 | 185 | + </section>  | 
 | 186 | + <section>  | 
 | 187 | + <title>HiddenHttpMethodFilter</title>  | 
 | 188 | + <para>The HiddenHttpMethodFilter should be placed before the Spring Security filter. In general this is true, but it could have additional implications when  | 
 | 189 | + protecting against CSRF attacks.</para>  | 
 | 190 | + <para>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  | 
 | 191 | + best practice to ensure it is placed before Spring Security's filters.</para>  | 
 | 192 | + </section>  | 
 | 193 | + </section>  | 
 | 194 | + <section>  | 
 | 195 | + <title>Overriding Defaults</title>  | 
 | 196 | + <para>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.</para>  | 
 | 197 | + <para>For example, you can provide a custom CsrfTokenRepository to override the way in which the <interfacename>CsrfToken</interfacename> is stored.</para>  | 
 | 198 | + <para>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  | 
 | 199 | + Spring Security's CSRF protection doesn't behave exactly as you want it, you are able to customize the behavior. Refer to the <link xlink:href="#nsa-csrf"><csrf /></link>  | 
 | 200 | + documentation for details on how to make these customizations with XML and the <classname>CsrfConfigurer</classname> javadoc for details on how to make these  | 
 | 201 | + customizations when using Java configuration.</para>  | 
 | 202 | + </section>  | 
 | 203 | +</chapter>  | 
0 commit comments