【问题标题】:Generate and validate CSRF token on java web application在 Java Web 应用程序上生成和验证 CSRF 令牌
【发布时间】:2021-11-27 02:29:56
【问题描述】:

我已要求实施 CSRF 以防止对 java 服务器应用程序的攻击。它是一个提供大量 Web REST API 服务的应用程序。 我查看了许多指南并在堆栈上搜索了here,但仍有一些顾虑。 我知道不需要 GET 请求。

所以,如果我错了,请纠正我。

  1. 浏览器首次发送请求以获取会话的 CSRF 令牌,大约 30 分钟
  2. 然后浏览器将此令牌放入脚本并发送到后端。

这是我的过滤器。

    public class ValidateCSRFToken implements Filter {

    private static final Logger LOGGER = LoggerFactory.getLogger(ValidateCSRFToken.class);

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
        HttpServletResponse httpServletResponse = (HttpServletResponse) servletResponse;
        // Spring put the CSRF token in session attribute "_csrf"
        CsrfToken csrfToken = (CsrfToken) httpServletRequest.getAttribute("_csrf");

        // Send the cookie only if the token has changed
        String actualToken = httpServletRequest.getHeader("X-CSRF-TOKEN");

        if (!StringUtils.isEmpty(csrfToken)) {
            LOGGER.info("CSRF token " + csrfToken.getToken());
        }

        if (!StringUtils.isEmpty(actualToken)) {
            LOGGER.info("X-CSRF-TOKEN " + actualToken);
        }

        if (actualToken == null || !actualToken.equals(csrfToken.getToken())) {
            String pCookieName = "CSRF-TOKEN";
            Cookie cookie = new Cookie(pCookieName, csrfToken.getToken());
            cookie.setMaxAge(-1);
            cookie.setHttpOnly(false);
            cookie.setPath("/");
            httpServletResponse.addCookie(cookie);
        }

        filterChain.doFilter(httpServletRequest, httpServletResponse);
    }
}

这是我的网络安全:

  //Start chain for restricting access.
    http.addFilterAfter(new ValidateCSRFToken(), CsrfFilter.class)
            .authorizeRequests()
            //The following paths…
            .antMatchers("**")
            // …are accessible to all users (authenticated or not).
            .permitAll()
            .antMatchers( "/actuator/**").permitAll()
            // All remaining paths…
            .anyRequest()
            // ...require user to at least be authenticated
            .authenticated()
            .and()// And if a user needs to be authenticated...
            .formLogin()
            // ...redirect them to /username`
            .loginPage("/username")
            .and()
            .logout().clearAuthentication(true).invalidateHttpSession(true).deleteCookies("JSESSIONID")
            .and()
            // If user isn't authorised to access a path...
            .exceptionHandling()
            // ...redirect them to /403
            .accessDeniedPage("/403")
            .and()
            .cors()
            .and()
            .csrf();

// .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse());;

所以,问题是:

  1. 如何验证不通过过滤链?
  2. 第一次浏览器自带第一个请求怎么办,也就是说,我们怎么知道这个请求属于浏览器而不是攻击者?

【问题讨论】:

  • 你不需要自己验证,spring security已经有一个CSRF过滤器,所以你可以去掉过滤器。其次,仅当您在服务器上更改某些内容时才需要发送令牌,因此在 POST、PUT、UPDATE 期间。回答你的第二个问题:你不知道,这就是为什么你有其他安全性,如 CORS、用户登录、令牌、会话 cookie、密码等。永远不要相信客户端或请求。你总是验证“你是谁(身份验证),你可以做你想做的事吗?(授权)”
  • 阅读 Spring Security 中的 CSRF 章节以及如何实现它。阅读关于 CSRF 攻击的 OWASP 以了解您在保护自己免受 cheatsheetseries.owasp.org/cheatsheets/… 的影响
  • 然后阅读 CSRF 上的 spring 安全参考章节docs.spring.io/spring-security/site/docs/current/reference/…
  • 假设浏览器第一次通过 Login API 向后端发送请求。然后这个请求仍然没有 CSRF 令牌(或没有??)和 Spring 生成令牌,但允许请求获取登录逻辑。那么,为什么 bowser 启动时,只发送获取令牌的请求而没有任何逻辑,而用户甚至在 borwser 中什么也不做。我们确信令牌已生成并发回。然后当用户想要登录时,浏览器已经在会话中拥有令牌并提出请求。换句话说,创建专用服务只是为了生成令牌。好吃吗?
  • 我想在之前的评论中说,第一次没有令牌传递到应用程序的逻辑内部。所以,是的,我需要一个过滤器来防止它并只返回令牌。

标签: java spring-boot spring-security csrf csrf-token


【解决方案1】:

我知道对于 GET 请求是不需要的。

这样做的目的是确保用户所做的任何具有任何副作用的操作实际上都是预期的交互。现在,GET 请求应该完全没有副作用,但是如果你搞砸了并且某些 GET 请求确实有副作用,那么你要么需要修复它,要么如果你不能,那么你的 GET 请求也需要CSRF 保护。这有点棘手,将令牌垃圾邮件发送到 URL 并不是一个好主意(这会发生)。

例如,如果您以管理员身份登录,只需点击https://yourserver.com/commands/deletePost?p=18 即可从数据库中删除unid 18 的文章,我可以创建一个站点:

<a href="https://yourserver.com/commands/deletePost?p=18">Click to see cute kittens!</a>

并发布。然后我就等你点击它,瞧。帖子没了。这就是 CSRF 的意义所在。

所以,浏览整个代码库。是否有任何 GET 请求导致状态更改的地方(创建或擦除的文件、数据库 INSERT 或 UPDATE 或 DELETE 命令、用于导致状态更改的 API,例如在 twitter 上发布内容或诸如此类的东西)。修复该代码并确保它们根本不允许这样做,并且仅适用于 POST/PUT。

然后将 CSRF 添加到所有这些 POST/PUT(或者,更好的是,只添加到所有 POST;考虑任何没有 CSRF 令牌的 POST,就好像用户没有登录一样)。

请注意,许多 Web 框架套件已经具备 CSRF 保护系统,包括 Spring Security。

【讨论】:

  • 是的,我不会使用 GET 请求对数据库进行任何更改。我已经检查过了。然后无论如何我需要实现这个逻辑来确定在哪里给出这个令牌,比如“如果方法不等于 GET,那么给出令牌”?这个实现应该做吗?
猜你喜欢
  • 2011-08-24
  • 1970-01-01
  • 2017-10-29
  • 1970-01-01
  • 2014-11-02
  • 2010-12-20
  • 2020-10-18
  • 1970-01-01
  • 2021-04-01
相关资源
最近更新 更多