Статьи

Защита WebSockets с использованием имени пользователя / пароля и безопасности сервлета

RFC 6455 предоставляет полный список соображений безопасности для WebSockets. Некоторые из них запечены в самом протоколе, а другие нуждаются в дополнительном объяснении того, как их можно достичь на конкретном сервере. Давайте поговорим о безопасности, встроенной в сам протокол:

  • Заголовок Origin в HTTP-запросе включает только информацию, необходимую для идентификации участника (веб-страницы, JavaScript или любого другого клиента), который инициировал запрос (обычно это схема, хост и порт инициирующего источника). Для WebSockets это поле заголовка включено в открывающее рукопожатие клиента. Это используется для информирования сервера о происхождении скрипта, генерирующего запрос соединения WebSocket. Затем сервер может принять решение принять или отклонить запрос квитирования соответственно. Это позволяет серверу защищать от несанкционированного использования сервера WebSocket из разных источников с помощью сценариев, использующих API WebSocket в браузере. Например, если образец Java EE 7 WebSocket Chat развернут в WildFly и доступен по адресу localhost: 8080 / chat / then заголовок источника — «http: // localhost: 8080». Клиенты без браузера могут использовать заголовок Origin для указания источника запроса. Серверы WebSocket должны быть осторожны при получении таких запросов.
  • Открывающее рукопожатие WebSocket от клиента должно содержать поля заголовка HTTP Sec-WebSocket-Key и Sec-WebSocket-Version. XMLHttpRequest может использоваться для создания HTTP-запросов и позволяет устанавливать заголовки как часть этого запроса как:
    01
    02
    03
    04
    05
    06
    07
    08
    09
    10
    11
    xhr.onreadystatechange = function ()
    {
      if (xhr.readyState == 4 && xhr.status == 200)
      {
        document.getElementById("myDiv").innerHTML = xhr.responseText;
      }
    }
    xhr.open("GET", "http://localhost:8080", true);
    xhr.setRequestHeader("foo", "bar");
    xhr.setRequestHeader("Sec-WebSocket-Key", "myKey");
    xhr.send();

    Если XMLHttpRequest пытается установить какие-либо поля заголовка, начиная с Sec-, то они игнорируются. Таким образом, злоумышленник не может имитировать соединение WebSocket с сервером с помощью API-интерфейсов HTML и JavaScript.

В дополнение к этим двум основным способам WebSockets можно защитить с помощью механизма аутентификации клиента, доступного для любых серверов HTTP. Этот технический совет покажет, как аутентифицировать WebSockets Java EE 7, развернутые в WildFly.

Давайте начнем!

  • Клон Java EE 7 Примеры рабочего пространства:
    1
    git clone https://github.com/javaee-samples/javaee7-samples.git
  • Пример «websocket / endpoint-security» показывает, как можно выполнить аутентификацию клиента до того, как клиент инициирует рукопожатие WebSocket. Это вызвано включением следующего дескриптора развертывания:
    01
    02
    03
    04
    05
    06
    07
    08
    09
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    <security-constraint>
      <web-resource-collection>
        <web-resource-name>WebSocket Endpoint</web-resource-name>
        <url-pattern>/*</url-pattern>
        <http-method>GET</http-method>
      </web-resource-collection>
      <auth-constraint>
        <role-name>g1</role-name>
      </auth-constraint>
    </security-constraint>
      
    <login-config>
      <auth-method>BASIC</auth-method>
      <realm-name>file</realm-name>
    </login-config>
      
    <security-role>
      <role-name>g1</role-name>
    </security-role>

    Некоторые ключевые моменты, чтобы понять об этом дескрипторе:

    • <url-pattern> указывает, что любой запрос к этому приложению будет запрашиваться для аутентификации
    • <auth-constraint> определяет роль безопасности, которая может получить доступ к этому ресурсу
    • <login-config> показывает, что файловая область используется с базовой аутентификацией
    • <security-role> определяет роли безопасности, на которые ссылается это приложение

    В нашем конкретном случае страница, которая создает соединение WebSocket, защищена базовой аутентификацией.

  • Загрузите WildFly 8.1 , разархивируйте и добавьте нового пользователя, запустив следующий скрипт:
    1
    ./bin/add-user.sh -a -u u1 -p p1 -g g1

    Это добавит пользователя «u1» с паролем «p1» в группу «g1». Указанная здесь группа должна соответствовать, как определено в <role-name> в дескрипторе развертывания.

  • Разверните образец, дав команду:
    1
    mvn wildfly:deploy

Теперь, когда к приложению обращаются по адресу localhost: 8080 / endpoint-security, появляется диалоговое окно безопасности, как показано:

techtip49-браузер безопасности всплывающих

Введите «u1» в качестве имени пользователя и «p1» в качестве пароля для аутентификации. Эти учетные данные определены в группе «g1», на которую ссылается дескриптор развертывания. Любые другие учетные данные будут продолжать возвращать диалог.

Как только запрос успешно аутентифицирован, соединение WebSocket устанавливается и в браузере отображается сообщение.

Если вы заинтересованы в защите только URL-адреса WebSocket, измените шаблон URL с:

1
 

чтобы:

1
/websocket

В файле websocket.js измените URL-адрес, чтобы создать конечную точку WebSocket из:

1
var wsUri = "ws://" + document.location.host + document.location.pathname + "websocket";

чтобы:

1
var wsUri = "ws://u1:p1@" + document.location.host + document.location.pathname + "websocket";

Обратите внимание, как учетные данные передаются в самом URL. Начиная с Google Chrome 38.0.2125.104, всплывающее окно браузера не отображается, если для проверки подлинности требуется только URL-адрес WebSocket.

Следующий технический совет объяснит, как защитить WebSocket с использованием протокола wss:// .