Spring Security Spring安全和过滤链

im9ewurl  于 2023-06-23  发布在  Spring
关注(0)|答案(1)|浏览(124)

早上好我正在为spring安全的过滤链和安全配置而挣扎

@Configuration
public class SecurityConfiguration {
    @Autowired
    private CustomHeaderVerificationFilter customHeaderFilter;
    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
            http
                    .addFilterBefore(customHeaderFilter, BasicAuthenticationFilter.class)
                    .authorizeRequests()
                    .requestMatchers(HttpMethod.GET, "/myapp/login").permitAll()
                    .anyRequest().permitAll();;

            return http.build();
        }
    }

正如你所看到的,逻辑将是这样的,每个调用都将被customHeaderFilter过滤,然后如果调用来自myapp/login作为httpGET,我将允许,不需要其他身份验证,否则...我允许所有(原因在前面)

@Component
public class CustomHeaderVerificationFilter   extends OncePerRequestFilter  {

    @Autowired
    Dao engine;
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
            String headerValue = request.getHeader("Authorization");
        try {
            if (headerValue != null && calculateMatch(headerValue.split("Bearer:")[1],request.getHeader("realmName"))) {
                filterChain.doFilter(request, response);
            } else {
              throw new NullPointerException();
            }
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException(e);
        } catch (NullPointerException e2){
            response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
            response.setContentType("application/json");
            response.getWriter().write("Errore credenziali errate");
        }
    }

        private boolean calculateMatch(String fromFront,String realmNameHeader) throws NoSuchAlgorithmException,NullPointerException {
        //prendo la secretdalBack

                String realmSecret = engine.getSecret(realmNameHeader);
                MessageDigest digest = MessageDigest.getInstance("SHA-256");
                String input = realmSecret + ":" + realmNameHeader;
                byte[] hashBytes = digest.digest(input.getBytes(StandardCharsets.UTF_8));
                StringBuilder hexString = new StringBuilder();
                for (byte b : hashBytes) {
                    String hex = Integer.toHexString(0xff & b);
                    if (hex.length() == 1) {
                        hexString.append('0');
                    }
                    hexString.append(hex);
                }
                String s = hexString.toString();
                return fromFront.equals(hexString.toString());

        }

}

(一个非常基本的非JWT承载过滤器,我想这不是问题所在)。
然而,我使用的每一个post调用,我都会收到一个未经授权的403错误。CSRF应该是“问题”,我发现的所有文档,他们只是跳过它禁用:但我会掌握它,并集成在我的应用程序。我不明白我的POST是否必须在GET调用之前请求csrf令牌,或者在调用的握手部分完成。我测试了各种方法,但其中很多都是utilyze .csrf(),它被弃用了,也有自定义过滤器,他们只是检查长度,但出于一个有趣的原因,只有我的get会进入这个过滤器。那么,流程中的错误在哪里?我需要通过逻辑检查CSRF滤波器?我必须保存它作为jwt代币吗?

2ledvvac

2ledvvac1#

你面对的错误是由于csrf配置,你需要在所有不安全的请求中有csrf令牌(任何改变状态的请求)
CSRF攻击之所以有效,是因为浏览器请求自动包含所有Cookie,包括会话Cookie。因此,如果用户通过了网站的身份验证,并且用户进入了一个邪恶的网站,这个邪恶的网站可以向我们的网站发送一个帖子请求(做任何事情),我们的网站无法区分合法的授权请求和伪造的身份验证请求。所以我们想给予用户一个唯一的令牌,他可以保存(远离cookie),用户在每个不安全的请求(即任何改变状态的请求)中使用该令牌。
何时以及如何获得CSRF代币?
1-在Spring Security 5中,默认行为是CsrfToken将在每个响应上加载。因此,默认情况下,您会在来自服务器的每个响应中获得一个XSRF-TOKEN cookie。
2-在Spring Security 6中,默认情况下,CsrfToken的查找将被推迟,直到需要它。所以cookie不会被设置,除非你发送一个状态更改请求(post,put,...),你可以克服这个问题,并使用以下配置为每个请求获取csrfToken。

@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
    
    CookieCsrfTokenRepository tokenRepository = CookieCsrfTokenRepository.withHttpOnlyFalse();
    CsrfTokenRequestAttributeHandler requestHandler = new CsrfTokenRequestAttributeHandler();
    // set the name of the attribute the CsrfToken will be populated on
    requestHandler.setCsrfRequestAttributeName("_csrf");
    http
        .csrf((csrf) -> csrf
            .csrfTokenRepository(tokenRepository)
            .csrfTokenRequestHandler(requestHandler)
        )
        .addFilterAfter(new CsrfCookieFilter(), BasicAuthenticationFilter.class);

    return http.build();
}

private static final class CsrfCookieFilter extends OncePerRequestFilter {

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
            throws ServletException, IOException {
        CsrfToken csrfToken = (CsrfToken) request.getAttribute(CsrfToken.class.getName());
        // Render the token value to a cookie by causing the deferred token to be loaded
        csrfToken.getToken();

        filterChain.doFilter(request, response);
    }
}

3-此外,您可以有一个返回csrf令牌的端点,并在需要时点击它。像这样

@GetMapping("/csrf")
public CsrfToken getCsrfToken(CsrfToken csrf) {
    return csrf;
}

所有3种都是可接受的解决方案。在第一个和第二个中,你从cookie中获取XSRF-TOKEN,在第三个中,你从响应主体中获取令牌,然后将其用作下一个不安全请求的X-CSRF-TOKEN头。
链接可能有助于:
cookieToHeader
https://docs.spring.io/spring-security/reference/features/exploits/csrf.html
https://docs.spring.io/spring-security/reference/5.8/migration/servlet/exploits.html#_defer_loading_csrftoken

相关问题