On the (Java) server side I wrote a servlet filter which checks that the cookie value and the HTTP header are specified and that they match:
public class CsrfDoubleSubmitCookieFilter implements Filter {
private static final Logger logger = LoggerFactory.getLogger(CsrfDoubleSubmitCookieFilter.class);
private static final String ANTI_CSRF_DOUBLE_SUBMIT_COOKIE = "Anti-Csrf-Double-Submit-Cookie";
@Override
public void destroy() {
}
@Override
public void init(FilterConfig config) throws ServletException {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
String antiCsrfHeader = request.getHeader(ANTI_CSRF_DOUBLE_SUBMIT_COOKIE);
String antiCsrfCookie = getCookie(request.getCookies(), ANTI_CSRF_DOUBLE_SUBMIT_COOKIE);
logger.debug("Checking CSRF cookie & header for: {}{}", request.getServletPath(), request.getPathInfo());
checkCrsfToken(antiCsrfHeader, antiCsrfCookie);
chain.doFilter(request, response);
}
/**
* Checks that the antiCsrfHeader and the antiCsrfCookie are not blank and do match
*/
private void checkCrsfToken(String antiCsrfHeader, String antiCsrfCookie) {
if (StringUtils.isBlank(antiCsrfHeader) || StringUtils.isBlank(antiCsrfCookie) || !antiCsrfHeader.equals(antiCsrfCookie)) {
logger.error("POSSIBLE CSRF (Cross-Site-Request-Forgery) ATTACK !!! -> Anti-Csrf-Header {} and Anti-Csrf-Cookie {} do not match",
antiCsrfHeader, antiCsrfCookie);
throw new WebSecurityException("");
}
}
private String getCookie(Cookie[] cookies, String name) {
String retVal = null;
for (int i = 0; i < cookies.length; i++) {
if (name.endsWith(cookies[i].getName())) {
retVal = cookies[i].getValue();
break;
}
}
return retVal;
}
}
On the Senche Touch side I added a 'beforerequest' listener function to Ext.Ajax in the launch-method of app.js. This will add a new CSRF token to the Anti-CSRF Cookie and Header for each Ajax request
launch: function () {
"use strict";
// Destroy the #appLoadingIndicator element
Ext.fly('appLoadingIndicator').destroy();
//Add double submit anti-CSRF cookie and header to each Ajax-request
Ext.Ajax.addListener( 'beforerequest', function(conn, options, eOpts) {
"use strict";
var csrfToken = Math.random();
document.cookie='Anti-Csrf-Double-Submit-Cookie=' + csrfToken + ';path=/';
options.headers['Anti-Csrf-Double-Submit-Cookie'] =csrfToken;
}
},
Note: When you keep state on the server side it is obviously more secure to let the server generate a new CSRF token for each user session and put it in the cookie. The client has to read the cookie value and set it into the header for each request. The server can than compare the CSRF token value from the server side session with the header value provided by the client.
No comments:
Post a Comment