Spring

JSESSIONID없이 StompCommand.CONNECT를 수행 할 수 있지만 X-XSRF-TOKEN이 필요한 이유는 무엇입니까?

기록만이살길 2021. 3. 14. 05:05
반응형

JSESSIONID없이 StompCommand.CONNECT를 수행 할 수 있지만 X-XSRF-TOKEN이 필요한 이유는 무엇입니까?

1. 질문(문제점):

내 목표는 WebSocket 끝점을 보호하는 것입니다 ws://localhost:8080/chat.

제가 한:

  1. STOMP와 WebSocket 연결을 만들려고했습니다.

    var socket = new SockJS("/chat");
    stompClient = Stomp.over(socket);
    
    stompClient.connect({"X-XSRF-TOKEN": getCookie("XSRF-TOKEN")}, function (status) {
      // it should not execute.
    });
    

    AbstractSecurityWebSocketMessageBrokerConfigurer

    // see: https://docs.spring.io/spring-security/site/docs/current/reference/html5/#websocket
    @Configuration
    public class WebSocketSecurityConfig extends AbstractSecurityWebSocketMessageBrokerConfigurer {
       @Override
       protected void configureInbound(MessageSecurityMetadataSourceRegistry messages) {
         messages
                .nullDestMatcher().authenticated()
                .simpDestMatchers("/app/**").hasRole("USER")
                .simpDestMatchers("/user/**", "/topic/channel/*").hasRole("USER")
                .simpTypeMatchers(MESSAGE, SUBSCRIBE).denyAll()
                .anyMessage().denyAll();
       }
    }
    

    WebSocketMessageBrokerConfigurer

    @Configuration
    @EnableWebSocketMessageBroker
    public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
      @Override
      public void registerStompEndpoints(StompEndpointRegistry registry) {
        registry.addEndpoint("/chat").withSockJS(); // user connect to
      }
    
      @Override
      public void configureMessageBroker(MessageBrokerRegistry registry) {
        registry.setApplicationDestinationPrefixes("/app"); // user send message to
        registry.enableSimpleBroker("/topic", "/queue"); // user subscribe to .. for channel messages.
        registry.setUserDestinationPrefix("/user"); // user subscribe to .. for private messages.
      }
    }
    

    WebSecurityConfigurerAdapter

    @Configuration
    public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
       @Override
       protected void configure(HttpSecurity http) throws Exception {
         http
    
     .csrf().csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
                 .and()
                 .formLogin()
                 .and()
                 .authorizeRequests().anyRequest().authenticated();
       }
    }
    

내 예상 결과는 다음과 같습니다. JSESSIONID를 보내지 않았기 때문에 WebSocket 연결이 실패해야합니다.

내 실제 결과는 다음과 같습니다 Spring Security은 사용자를 인식하고 난을 얻을 수 있습니다 UserDetails(UserDetails) SecurityContextHolder.getContext().getAuthentication().getPrincipal().

내 질문 :

  1. 데이터베이스를 사용하려면 JSESSIONID 또는 XSRF-TOKEN?

  2. X-XSRF-TOKENStompCommand.CONNECT 엔드 포인트를 보호하는 올바른 방법을 사용 하고 있습니까?

  3. Spring Security X-XSRF-TOKENJSESSIONID. CSRF 토큰은 사이트 간 요청 위조를 방지하는 데 사용되지 않습니까?

    X-XSRF-TOKEN요청 헤더에 키 없이 WebSocket 연결을 생성하면 브라우저 콘솔에서 오류가 발생 합니다.

    <<< ERROR
    message:Failed to send message to ExecutorSubscribableChannel[clientInboundChannel]; nested exception 
    is org.springframework.security.web.csrf.InvalidCsrfTokenException\c Invalid CSRF Token 'null' was 
    found on the request parameter '_csrf' or header 'X-XSRF-TOKEN'.
    content-length:0
    

2. 해결방안:

먼저 WS 연결을 도와 드리겠습니다. 귀하와 유사한 구성을 시도했지만 권한이없는 WS에 연결할 수 없습니다. 브라우저에 JSESSIONID 쿠키가 없거나 쿠키가 있으면 익명 세션에 연결되었음을 확인했습니다. org.springframework.security의 로깅을 TRACE로 설정하고 작성된 로그를 따르십시오.이 경우 액세스 권한이 부여 된 필터를 결정하는 데 도움이됩니다.

지정한 질문의 경우 :

  1. JSESSIONID와 XSRF-TOKEN은 서로 바꿔서 사용할 수 없습니다. 동일한 JSESSIONID에 대해 서로 다른 XSRF-TOKEN 쿠키를받을 수 있다고 생각합니다. 그래서 세션 ID를 저장해야한다고 생각합니다. Spring에는 이것을위한 프로젝트가있다 : Spring Session JDBC

  2. 그렇다고 생각합니다. Spring Security조차도 기본적으로 기대합니다. 스프링 Security 웹 소켓

모든 인바운드 CONNECT 메시지에는 동일한 출처 정책을 적용하기 위해 유효한 CSRF 토큰이 필요합니다.

  1. 교차 사이트 요청 위조에 대해 얼마나 잘 알고 있는지 잘 모르겠습니다. 그렇지 않은 경우이 Spring CSRF를 확인하십시오 .

JSESSIONID는 쿠키이며 다른 사이트에서도 귀하의 사이트에 대한 모든 요청과 함께 전송됩니다. 그것이 브라우저가 작동하는 방식이기 때문입니다. 따라서 세션의 오용을 방지하기 위해 CSRF 토큰이 필요합니다. 양식 제출과 같은 작업을 실행하는 데 HTML을 사용하지 않기 때문에이 토큰을 다른 방법으로 전달해야합니다. Spring은 X-XSRF-TOKEN이라는 헤더를 사용하여 보내야합니다. 브라우저는 쿠키처럼 모든 요청과 함께 보내지 않기 때문입니다. Security은 취약점을 악용하려는 다른 사이트에 있으며, 귀하의 사이트에서 쿠키를 읽을 수 없으며, 공격 요청을 위해 헤더에 CSRF를 추가하는 데 사용할 수 없습니다.

그래서 나는 당신의 해결책이 괜찮다고 생각합니다. WS가 핸드 셰이크에 HTTP를 사용하더라도 HTTP와 WS는 서로 다른 프로토콜이지만 여러 헤더를 추가합니다. 아마도 Spring이 권한 검사를 무시하기 때문에 아마도 당신이 뭔가를 놓친 것 같고 그 Spring Security 추적 로그가 무엇을 검사할지 지시합니다.

65702853
반응형