Введение в безопасность и WebSockets

Введение в безопасность и WebSockets

1. Вступление

Вprevious article мы показали, как добавить WebSockets в проект Spring MVC.

Здесь мы опишем, какadd security to Spring WebSockets in Spring MVC. Прежде чем продолжить, убедитесь, что у вас уже есть базовое покрытие Spring MVC Security - если нет, проверьтеthis article.

2. Maven Зависимости

Нам нужныtwo main groups of Maven dependencies для нашей реализации WebSocket.

Во-первых, давайте укажем всеобъемлющие версии Spring Framework и Spring Security, которые мы будем использовать:


    5.0.6.RELEASE
    5.0.6.RELEASE

Во-вторых, давайте добавим основные библиотеки Spring MVC и Spring Security, необходимые для реализации базовой аутентификации и авторизации:


    org.springframework
    spring-core
    ${spring.version}


    org.springframework
    spring-web
    ${spring.version}


    org.springframework
    spring-webmvc
    ${spring.version}


    org.springframework.security
    spring-security-web
    ${spring-security.version}


    org.springframework.security
    spring-security-config
    ${spring-security.version}

Последние версииspring-core,spring-web,spring-webmvc,spring-security-web,spring-security-config можно найти на Maven Central.

Наконец, давайте добавим необходимые зависимости:


    org.springframework
    spring-websocket
    ${spring.version}


    org.springframework
    spring-messaging
    ${spring.version}


    org.springframework.security
    spring-security-messaging
    ${spring-security.version}

Вы можете найти последнюю версиюspring-websocket,spring-messaging иspring-security-messaging на Maven Central.

3. Базовая безопасность WebSocket

Специфичная для WebSocket безопасность с использованием библиотекиspring-security-messaging сосредоточена на классеAbstractSecurityWebSocketMessageBrokerConfigurer и его реализации в вашем проекте:

@Configuration
public class SocketSecurityConfig
  extends AbstractSecurityWebSocketMessageBrokerConfigurer {
      //...
}

КлассAbstractSecurityWebSocketMessageBrokerConfigurerprovides additional security coverage, предоставляемыйWebSecurityConfigurerAdapter.

Библиотекаspring-security-messaging - не единственный способ реализовать безопасность для WebSockets. Если мы будем придерживаться обычной библиотекиspring-websocket, мы сможем реализовать интерфейсWebSocketConfigurer и прикрепить перехватчики безопасности к нашим обработчикам сокетов.

Поскольку мы используем библиотекуspring-security-messaging, мы будем использовать подходAbstractSecurityWebSocketMessageBrokerConfigurer.

3.1. РеализацияconfigureInbound()

РеализацияconfigureInbound() - самый важный шаг в настройке подклассаAbstractSecurityWebSocketMessageBrokerConfigurer:

@Override
protected void configureInbound(
  MessageSecurityMetadataSourceRegistry messages) {
    messages
      .simpDestMatchers("/secured/**").authenticated()
      .anyMessage().authenticated();
}

В то время какWebSecurityConfigurerAdapter позволяет указать различные требования к авторизации в масштабе приложения для разных маршрутов,AbstractSecurityWebSocketMessageBrokerConfigurer позволяет указать конкретные требования к авторизации для назначений сокетов.

3.2. Соответствие типа и назначения

MessageSecurityMetadataSourceRegistry позволяет нам указывать ограничения безопасности, такие как пути, роли пользователей и разрешенные сообщения.

Type matchers constrain which SimpMessageType are allowed и каким образом:

.simpTypeMatchers(CONNECT, UNSUBSCRIBE, DISCONNECT).permitAll()

Destination matchers constrain which endpoint patterns are accessible и каким образом:

.simpDestMatchers("/app/**").hasRole("ADMIN")

Subscribe destination matchers map a ListofSimpDestinationMessageMatcher instancesthat match on SimpMessageType.SUBSCRIBE:

.simpSubscribeDestMatchers("/topic/**").authenticated()

4. Защита маршрутов сокетов

Теперь, когда мы познакомились с базовой безопасностью сокетов и конфигурацией сопоставления типов, мы можем комбинировать безопасность сокетов, представления, STOMP (протокол обмена текстовыми сообщениями), брокеры сообщений и контроллеры сокетов, чтобы включить безопасные веб-сокеты в нашем приложении Spring MVC.

Во-первых, давайте настроим наши представления сокетов и контроллеры для базового покрытия Spring Security:

@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
@EnableWebSecurity
@ComponentScan("com.example.springsecuredsockets")
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
          .authorizeRequests()
          .antMatchers("/", "/index", "/authenticate").permitAll()
          .antMatchers(
            "/secured/**/**",
            "/secured/success",
            "/secured/socket",
            "/secured/success").authenticated()
          .anyRequest().authenticated()
          .and()
          .formLogin()
          .loginPage("/login").permitAll()
          .usernameParameter("username")
          .passwordParameter("password")
          .loginProcessingUrl("/authenticate")
          //...
    }
}

Во-вторых, давайте настроим фактическое место назначения сообщения с требованиями аутентификации:

@Configuration
public class SocketSecurityConfig
  extends AbstractSecurityWebSocketMessageBrokerConfigurer {
    @Override
    protected void configureInbound(MessageSecurityMetadataSourceRegistry messages) {
        messages
          .simpDestMatchers("/secured/**").authenticated()
          .anyMessage().authenticated();
    }
}

Теперь в нашем WebSocketMessageBrokerConfigurer, мы можем зарегистрировать фактическое сообщение и конечные точки STOMP:

@Configuration
@EnableWebSocketMessageBroker
public class SocketBrokerConfig
  implements WebSocketMessageBrokerConfigurer {

    @Override
    public void configureMessageBroker(MessageBrokerRegistry config) {
        config.enableSimpleBroker("/secured/history");
        config.setApplicationDestinationPrefixes("/spring-security-mvc-socket");
    }

    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        registry.addEndpoint("/secured/chat")
          .withSockJS();
    }
}

Давайте определимan example socket controller и конечную точку, для которой мы обеспечили безопасность выше:

@Controller
public class SocketController {

    @MessageMapping("/secured/chat")
    @SendTo("/secured/history")
    public OutputMessage send(Message msg) throws Exception {
        return new OutputMessage(
           msg.getFrom(),
           msg.getText(),
           new SimpleDateFormat("HH:mm").format(new Date()));
    }
}

5. Политика единого происхождения

Same Origin Policy требует, чтобы все взаимодействия с конечной точкой происходили из того же домена, в котором взаимодействие было инициировано.

Например, предположим, что ваша реализация WebSockets размещена вfoo.com, а выenforcing same origin policy. Если пользователь подключается к вашему клиенту, размещенному наfoo.com, а затем открывает другой браузер наbar.com, тоbar.com не будет иметь доступа к вашей реализации WebSocket.

5.1. Отмена той же политики происхождения

Spring WebSockets сразу же применяет ту же политику происхождения, а обычные WebSockets - нет.

Фактически,Spring Security requires a CSRF (Cross Site Request Forgery) token для любого допустимого типа сообщенияCONNECT:

@Controller
public class CsrfTokenController {
    @GetMapping("/csrf")
    public @ResponseBody String getCsrfToken(HttpServletRequest request) {
        CsrfToken csrf = (CsrfToken) request.getAttribute(CsrfToken.class.getName());
        return csrf.getToken();
    }
}

Вызывая конечную точку в/csrf, клиент может получить токен и пройти аутентификацию через уровень безопасности CSRF.

ОднакоSame Origin Policy for Spring can be overridden, добавив следующую конфигурацию в вашAbstractSecurityWebSocketMessageBrokerConfigurer:

@Override
protected boolean sameOriginDisabled() {
    return true;
}

5.2. STOMP, поддержка SockJS и параметры фрейма

ОбычноSTOMP вместе сSockJS используются для реализации клиентской поддержки Spring WebSockets.

SockJS is configured to disallow transports through HTML iframe elements by default. This is to prevent the threat of clickjacking.

Однако есть определенные варианты использования, в которых разрешениеiframes использовать транспорты SockJS может быть полезным. Для этого вы можете изменить конфигурацию по умолчанию вWebSecurityConfigurerAdapter:

@Override
protected void configure(HttpSecurity http)
  throws Exception {
    http
      .csrf()
        //...
        .and()
      .headers()
        .frameOptions().sameOrigin()
      .and()
        .authorizeRequests();
}

Обратите внимание, что в этом примере мы следуемSame Origin Policy, несмотря на то, что разрешены передачи черезiframes.

6. Покрытие Oauth2

Специфичная для Oauth2 поддержка Spring WebSockets стала возможной благодаря реализации покрытия безопасности Oauth2 в дополнение к вашему стандартному покрытиюWebSecurityConfigurerAdapter.Here’s - и за счет расширения - примера того, как реализовать Oauth2.

Чтобы аутентифицировать и получить доступ к конечной точке WebSocket, вы можете передать Oauth2access_token в параметр запроса при подключении вашего клиента к вашему внутреннему WebSocket.

Вот пример, демонстрирующий эту концепцию с использованием SockJS и STOMP:

var endpoint = '/ws/?access_token=' + auth.access_token;
var socket = new SockJS(endpoint);
var stompClient = Stomp.over(socket);

7. Заключение

В этом кратком руководстве мы показали, как повысить безопасность Spring WebSockets. Взгляните на справочную документацию SpringWebSocket иWebSocket Security, если вы хотите узнать больше об этой интеграции.

Как всегда, проверьтеour Github project на примеры, использованные в этой статье.