使用方法
this.tokenRepository.loadToken(request)
从请求中加载 csrfToken,并且也使用方法 String actualToken = request.getHeader(csrfToken.getHeaderName())
从请求中获取actualToken。那csrfToken和actualToken不一样吗?
代码片段
if (!csrfToken.getToken().equals(actualToken)) {}
有什么作用?
@Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
request.setAttribute(HttpServletResponse.class.getName(), response);
CsrfToken csrfToken = this.tokenRepository.loadToken(request);
final boolean missingToken = csrfToken == null;
if (missingToken) {
csrfToken = this.tokenRepository.generateToken(request);
this.tokenRepository.saveToken(csrfToken, request, response);
}
request.setAttribute(CsrfToken.class.getName(), csrfToken);
request.setAttribute(csrfToken.getParameterName(), csrfToken);
if (!this.requireCsrfProtectionMatcher.matches(request)) {
filterChain.doFilter(request, response);
return;
}
String actualToken = request.getHeader(csrfToken.getHeaderName());
if (actualToken == null) {
actualToken = request.getParameter(csrfToken.getParameterName());
}
if (!csrfToken.getToken().equals(actualToken)) {
if (this.logger.isDebugEnabled()) {
this.logger.debug("Invalid CSRF token found for "
+ UrlUtils.buildFullRequestUrl(request));
}
if (missingToken) {
this.accessDeniedHandler.handle(request, response,
new MissingCsrfTokenException(actualToken));
}
else {
this.accessDeniedHandler.handle(request, response,
new InvalidCsrfTokenException(csrfToken, actualToken));
}
return;
}
filterChain.doFilter(request, response);
}
CsrfFilter 是 Web 应用程序中使用的一种安全机制,用于防止跨站点请求伪造 (CSRF) 攻击。当恶意网站诱骗用户的浏览器在用户经过身份验证的另一个网站上执行不需要的操作时,就会发生 CSRF 攻击。这可能会导致未经用户同意而代表用户执行未经授权的操作。
以下是 CsrfFilter 通常如何防止此类攻击:
令牌生成:当用户访问网页时,服务器会为会话生成唯一的 CSRF 令牌,并将其作为隐藏字段、元标记或 URL 的一部分包含在网页中。
令牌提交:当用户提交表单或执行需要状态更改的操作(例如发布评论、购买等)时,CSRF 令牌将包含在请求中(作为隐藏表单字段或作为一部分) HTTP 标头)。
令牌验证:收到请求后,服务器会根据用户会话中存储的令牌检查 CSRF 令牌。如果令牌匹配,则该请求被视为合法并被处理。如果令牌不匹配,则请求被拒绝。
错误处理:如果 CSRF 令牌丢失或无效,可以将 CsrfFilter 配置为返回错误响应、重定向到错误页面或采取其他适当的操作来通知用户无效请求。
CsrfFilter的主要特点: 基于会话的令牌:令牌通常与用户会话绑定,确保每个用户获得一个唯一的令牌,攻击者无法猜测或重复使用。 隐藏字段:CSRF 令牌通常作为隐藏字段嵌入到表单中,以确保它们与表单提交一起发送。 标头令牌:对于 AJAX 请求,CSRF 令牌可以包含在自定义 HTTP 标头中。 令牌轮换:某些实现会定期轮换 CSRF 令牌以增强安全性。 可配置:许多 CSRF 过滤器实现都是可配置的,允许开发人员指定哪些请求需要令牌验证、自定义错误处理等。 实施示例: 下面是如何在 Java 中实现 CSRF 过滤器的简化示例(例如,在基于 Spring 的应用程序中):
生成代币:
java 复制代码 @成分 公共类 CsrfTokenGeneratorFilter 扩展了 OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
HttpSession session = request.getSession();
CsrfToken csrfToken = (CsrfToken) session.getAttribute("csrfToken");
if (csrfToken == null) {
csrfToken = new DefaultCsrfToken("X-CSRF-TOKEN", "_csrf", UUID.randomUUID().toString());
session.setAttribute("csrfToken", csrfToken);
}
request.setAttribute(CsrfToken.class.getName(), csrfToken);
filterChain.doFilter(request, response);
}
} 验证令牌:
java 复制代码 @成分 公共类 CsrfTokenValidationFilter 扩展了 OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
CsrfToken csrfToken = (CsrfToken) request.getAttribute(CsrfToken.class.getName());
String actualToken = request.getHeader(csrfToken.getHeaderName());
if (actualToken == null) {
actualToken = request.getParameter(csrfToken.getParameterName());
}
if (csrfToken.getToken().equals(actualToken)) {
filterChain.doFilter(request, response);
} else {
response.sendError(HttpServletResponse.SC_FORBIDDEN, "CSRF token validation failed");
}
}
} 在此示例中,CsrfTokenGeneratorFilter 将创建一个 CSRF 令牌(如果不存在)并将其存储在用户的会话中。 CsrfTokenValidationFilter 根据会话存储的令牌检查传入请求中的令牌,确保仅处理有效的请求。
此设置可确保用户有意发出状态更改请求,从而有助于防止 CSRF 攻击。