简体   繁体   English

在 java web 应用程序上生成并验证 CSRF 令牌

[英]Generate and validate CSRF token on java web application

I have asked to implement CSRF prevent attacking on java server application.我已经要求实施 CSRF 以防止对 java 服务器应用程序的攻击。 It's an application that serves a lot of web REST API services.它是一个提供大量 web REST API 服务的应用程序。 I looked at many guides and searched here on stack, but still have some concerns.我看了很多指南并在堆栈上搜索了这里,但仍然有一些顾虑。 I understand that for GET requests is not needed.我知道不需要 GET 请求。

So, correct me if i'm wrong.所以,如果我错了,请纠正我。

  1. Browser send request for the first time to get CSRF token for session that about 30 minutes浏览器第一次发送请求获取 session 的 CSRF token 大约 30 分钟
  2. Then Browser puts this token into script and sends to Back-End.然后浏览器将这个令牌放入脚本并发送到后端。

This is my filter.这是我的过滤器。

    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);
    }
}

This is my WebSecurity:这是我的网络安全:

  //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());; //.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse());;

So, the questions are:所以,问题是:

  1. How to validate it and not to pass through filter chain?如何验证它而不通过过滤器链?
  2. How to do that for the first time when browser comes with the first request, in other words, how do we know that this request belongs to browser and not to attackers?当浏览器第一次请求时,第一次怎么办,换句话说,我们怎么知道这个请求是属于浏览器的,而不是属于攻击者的?

I understand that for GET requests is not needed.我知道不需要 GET 请求。

The point of it is to ensure anything the user does that has any side effect, is actually an intended interaction.它的重点是确保用户所做的任何有副作用的事情实际上都是预期的交互。 Now, GET requests should have no side effects at all, but if you messed that up and some GET requests do have side effects, then you either need to fix that, or if you can't, then your GET requests also need CSRF protection.现在, GET请求应该完全没有副作用,但是如果你搞砸了并且某些 GET 请求确实有副作用,那么你要么需要修复它,要么如果你不能,那么你的 GET 请求也需要 CSRF 保护. Which is a bit tricky, it's not entirely a good idea to spam the token into a URL (which is what would happen).这有点棘手,将令牌垃圾邮件发送到 URL 并不是一个好主意(这是会发生的事情)。

For example, if just hitting https://yourserver.com/commands/deletePost?p=18 deletes the article with unid 18 from the database if you're logged in as admin, I can make a site with:例如,如果您以管理员身份登录,只需点击https://yourserver.com/commands/deletePost?p=18从数据库中删除 unid 18 的文章,我可以创建一个站点:

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

and post it.并发布它。 Then I just wait for your to click it and voila.然后我就等着你点击它,瞧。 Post is gone.帖子没了That's what CSRF is about.这就是 CSRF 的意义所在。

So, go through the entire code base.因此,go 遍历了整个代码库。 Is there any place in it where any GET request causes state changes (files created or wiped, database INSERT or UPDATE or DELETE commands, APIs used to cause state changes such as posting stuff on twitter or whatnot - that sort of thing).其中是否有任何 GET 请求导致 state 更改的地方(创建或擦除的文件、数据库 INSERT 或 UPDATE 或 DELETE 命令、用于导致 state 更改的 API,例如在 twitter 上发布内容或类似的东西)。 Fix that code and ensure they simply do not allow that and only work on POST/PUT.修复该代码并确保他们根本不允许这样做并且只在 POST/PUT 上工作。

Then add CSRF to all those POSTs/PUTs (or, better yet, just to all POSTs; consider any POST without CSRF tokens as if the user wasn't logged in).然后将 CSRF 添加到所有这些 POST/PUT(或者,更好的是,只添加到所有 POST;考虑任何没有 CSRF 令牌的 POST,就好像用户没有登录一样)。

Note that many web framework kits already have CSRF protection systems in place, including Spring Security.请注意,许多 web 框架套件已经具备 CSRF 保护系统,包括 Spring Security。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM