I studied Spring Security (and interface development) with a few tutorials. However, I am very confused by CSRF tokens, and I am clearly doing something wrong.
My Spring Security is configured using java, and when I disable CSRF (using the following snippet), the form submits without problems.
http.csrf().disable();
My understanding from here is that the steps I need to follow are as follows:
1) Use proper verbs
2) Enable csrf protection
3) include _csrf as hidden fields in form.
All these steps sound simple, but they don't seem to work for me, and I get an error:
HTTP Status 403 - Invalid CSRF Token 'null' was found on the request parameter '_csrf' or header 'X-CSRF-TOKEN'.
when I try to submit my registration form.
, "_csrf" . , , , , , .
:
<form method="POST" th:object="${individualRegistrationInfo}">
<table>
<tr>
<td>Name:</td>
<td><input type="text" th:field="*{name}" /></td>
<td th:if="${#fields.hasErrors('name')}"><p th:errors="*{name}">Incorrect Name</p></td>
</tr>
<tr>
<td>Username:</td>
<td><input type="text" th:field="*{username}" /></td>
<td th:if="${#fields.hasErrors('username')}"><p th:errors="*{username}">Incorrect Username</p></td>
</tr>
<tr>
<td>Password:</td>
<td><input type="password" th:field="*{password}" /></td>
<td th:if="${#fields.hasErrors('password')}"><p th:errors="*{password}">Incorrect Password</p></td>
</tr>
<tr>
<td>Email:</td>
<td><input type="email" th:field="*{email}" /></td>
<td th:if="${#fields.hasErrors('email')}"><p th:errors="*{email}">Incorrect Email</p></td>
</tr>
<tr>
<td>Confirm Email:</td>
<td><input type="email" th:field="*{confirmEmail}" /></td>
<td th:if="${#fields.hasErrors('confirmEmail')}"><p th:errors="*{confirmEmail}">Incorrect Email Confirmation</p></td>
</tr>
<tr>
<td>Region:</td>
<td><select th:field="*{regionName}">
<option value="NONE">----Select----</option>
<option th:each="region : ${regions}" th:value="${region}" th:text="${region}">RegionTemplate</option>
</select></td>
<td th:if="${#fields.hasErrors('regionName')}"><p th:errors="*{regionName}">Region Name</p></td>
</tr>
<tr><td>
<span th:text= "${_csrf.parameterName}">CSRF Parm Name</span></td>
<td> <span th:text= "${_csrf.token}">CSRF Token value</span> </td></tr>
<tr>
<td colspan="3">
<input type="submit" value="Register" />
</td>
</tr>
<input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}"/>
</table>
</form>
, , :
" , CSRF HttpSession, , HttpSession, AccessDeniedHandler InvalidCsrfTokenException. AccessDeniedHandler , HTTP 403 ".
HttpSession, , - ( ). , , , - .
gradle:
compile group: 'org.springframework', name: 'spring-webmvc', version:'4.0.5.RELEASE'
compile group: 'org.springframework', name: 'spring-context-support', version:'4.0.5.RELEASE'
compile group: 'org.springframework.boot', name: 'spring-boot-starter-data-jpa', version:'1.1.4.RELEASE'
compile group: 'org.springframework.boot', name: 'spring-boot-starter-thymeleaf', version:'1.1.4.RELEASE'
compile group: 'org.springframework.boot', name: 'spring-boot-starter-security', version:'1.1.4.RELEASE'
testCompile group: 'org.springframework.security', name:'spring-security-test', version:'4.0.0.M1'
, :
1) Use proper verbs (POST is clearly visible on the form code snippet)
2) Enable csrf protection (http.csrf().disable(); is commented out and _CSRF shows in form)
3) include _csrf as hidden fields in form. (clearly visible on the form code snippet)
, ,
4) I am missing something !
- , ?
, , .
29
, CSRF.
, AccessDeniedHandler, .
.
, _csrf, , AccessDeniedHandler.
:
static class CustomAccessDeniedHandler implements AccessDeniedHandler {
@Override
public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException)
throws IOException, ServletException {
logger.warn("Arrived in custom access denied handler.");
HttpSession session = request.getSession();
System.out.println("Session is " +session );
System.out.println("Session id = " + session.getId());
System.out.println("Session max interval="+session.getMaxInactiveInterval());
System.out.println("Session last used="+session.getLastAccessedTime());
System.out.println("Time now="+new Date().getTime());
System.out.println();
System.out.println("csrf:");
Object csrf = request.getAttribute("_csrf");
if (csrf==null) {
System.out.println("csrf is null");
} else {
System.out.println(csrf.toString());
if (csrf instanceof DefaultCsrfToken) {
DefaultCsrfToken token = (DefaultCsrfToken) csrf;
System.out.println("Parm name " + token.getParameterName());
System.out.println("Token " + token.getToken());
}
}
System.out.println();
System.out.println("Request:");
System.out.println(request.toString());
System.out.println();
System.out.println("Response:");
System.out.println(response.toString());
System.out.println();
System.out.println("Exception:");
System.out.println(accessDeniedException.toString());
}
}
:
Session is org.apache.catalina.session.StandardSessionFacade@579ebbb8
Session id = 7CC03DAFF6BC34E28F5E91974C7E4BA5
Session max interval=1800
Session last used=1406630832436
Time now=1406630878254
csrf:
org.springframework.security.web.csrf.DefaultCsrfToken@763659f8
Parm name _csrf
Token 1e9cb3cf-c111-4b05-aace-4f8480b7d67b
Request:
org.springframework.security.web.context.HttpSessionSecurityContextRepository$Servlet3SaveToSessionRequestWrapper@6a4ce569
Response:
org.springframework.security.web.context.HttpSessionSecurityContextRepository$SaveToSessionResponseWrapper@5e698704
Exception:
org.springframework.security.web.csrf.InvalidCsrfTokenException: Invalid CSRF Token 'null' was found on the request parameter '_csrf' or header 'X-CSRF-TOKEN'.
1e9cb3cf-c111-4b05-aace-4f8480b7d67b .
, CSRF "null", , "_csrf".
30
, GitHub:
https://github.com/Mark-Allen/csrf-example.git
.
52 SecurityWebConfiguration (. , ), .
http.csrf().disable();
@Bart, , .
, - , .