我读了很多关于Spring Securitys CSRF保护的内容,但我仍然有点挣扎。现在文档像往常一样很棒,但它完全基于你在服务器上呈现html代码并且能够为每个表单添加隐藏字段的想法。现在,因为我使用AngularJS和JavaScript来调用后端,所以这不是一个真正的选择。
那么在这种情况下实际获取令牌到客户端的最佳方法是什么(Rest Backend / AngularJS前端)? AngularJS似乎已经在$ resource中建立了对CSRF的支持,并期望一个名为" XSRF-TOKEN"检索令牌并将其作为http标头发送" X-XSRF-TOKEN"在进一步的要求。因此,每个请求都将包含http标头以及cookie。现在在服务器端,我可以读取标题并将其与存储在会话中的令牌进行比较。
我有这个问题,它看起来有点复杂。由于登录本身必须受到保护,因此需要创建一个临时会话,仅用于CSRF令牌。这真的有必要吗?
也许这只是一个愚蠢的问题,但为什么我不能在客户端创建一个随机令牌并在客户端将其设置为HTTP头和cookie。这与" OWASP double submit cookie"类似,但在客户端生成令牌。这样服务器就不需要在登录前进行会话,因为他可以只比较2个提交的令牌。现在虽然攻击者可以发送HTTP标头,但他会根据同源策略无法读取或设置cookie,只要该数字实际上是不可知的,就无法获得匹配。
现在本能地在客户端生成安全令牌对我来说似乎很危险,我想我可以避免它......但是为什么?我觉得我错过了一些东西,肯定有一个很好的理由让SpringSecurity将令牌存储在会话中,对吗?
请赐教:)
答案 0 :(得分:1)
我最终使用spring-security-csrf-token-interceptor-extended,它从http-header“X-CSRF-TOKEN”(名称是可配置的)读取CSRF-Token,并在进一步请求时将其作为http-header发送。
现在我唯一要做的就是让Spring-Security将Token作为HTTP Header发送(因为我不在服务器端渲染HTML代码,因此无法将其添加为隐藏字段)。
<security:http ....
<security:custom-filter ref="csrfTokenResponseHeaderBindingFilter" after="CSRF_FILTER"/>
....
</security:http>
过滤器基本上在正常CSRF_FILTER之后运行并读取“_csrf”请求属性(由CSRF_FILTER放置)并将其设置为标题“X-CSRF-TOKEN”
public class CsrfTokenResponseHeaderBindingFilter extends OncePerRequestFilter {
protected static final String REQUEST_ATTRIBUTE_NAME = "_csrf";
protected static final String RESPONSE_TOKEN_NAME = "X-CSRF-TOKEN";
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, javax.servlet.FilterChain filterChain) throws ServletException, IOException {
CsrfToken token = (CsrfToken) request.getAttribute(REQUEST_ATTRIBUTE_NAME);
if (token != null) {
response.setHeader(RESPONSE_TOKEN_NAME, token.getToken());
}
filterChain.doFilter(request, response);
}
}