Поделиться через


Как использовать проверку подлинности в Web PubSub для Socket.IO

Общие сведения

протокол Socket.IO — это протокол уровня приложений, который основан на протоколе транспортного слоя с именем Engine.IO протоколе. Engine.IO отвечает за установку низкоуровневого подключения между сервером и клиентом. Подключение Engine.IO управляет ровно одним реальным подключением, которое является подключением по протоколу HTTP long-polling или подключением WebSocket.

Собственный механизм проверки подлинности, предоставляемый библиотекой Socket.IO, применяется на уровне подключения Socket.IO. Подключение Engine.IO уже создано успешно до того, как проверка подлинности вступает в силу. Базовое Engine.IO подключение можно создать между клиентом и сервером без какого-либо механизма проверки подлинности. Злоумышленники могут использовать подключение Engine.IO без проверки подлинности для использования ресурса клиента без каких-либо ограничений.

Внимание

Необработанные строки подключения указаны в этой статье только в демонстрационных целях.

Строка подключения содержит сведения об авторизации, требуемые для доступа приложения к службе Azure Web PubSub. Ключ доступа в строке подключения аналогичен паролю привилегированного пользователя для службы. В рабочих средах всегда защищать ключи доступа. Используйте Azure Key Vault для безопасного управления и ротации ключей, а также для защиты вашего подключения с помощью WebPubSubServiceClient.

Старайтесь не распространять ключи доступа среди других пользователей, жестко программировать их или где-то сохранять в виде обычного текста в открытом доступе для других пользователей. Замените свои ключи, если считаете, что они могли быть скомпрометированы.

Проверка подлинности для подключения Socket.IO

Этот уровень проверки подлинности не рекомендуется использовать в рабочей среде. Поскольку не предоставляется никакой защиты для низкоуровневого подключения Engine.IO, ваш ресурс становится легко уязвимым для атак.

Проверка подлинности для подключения Engine.IO

Этот уровень проверки подлинности рекомендуется для защиты подключения Engine.IO. Сейчас Socket.IO библиотека не предоставляет такой механизм проверки подлинности для Engine.IO подключений. Пакет SDK для сервера SocketIO Azure внедряет механизм согласования и предоставляет интерфейсы API для использования.

Клиент отправляет запрос на согласование, содержащий сведения о проверке подлинности на сервер до создания подключения Engine.IO. Ниже приведены сведения о том, как работает механизм:

  1. Перед подключением к конечной точке службы клиент отправляет переговоры на сервер, который несет сведения, необходимые для проверки подлинности.
  2. Сервер получает запрос на согласование, анализирует сведения проверки подлинности и проверяет подлинность клиента в соответствии с проанализированной информацией. Затем сервер отвечает на запрос маркером доступа в формате JWT .
  3. Клиент подключается к конечной точке службы с маркером доступа, выданным сервером. Токен доступа должен быть помещен в строку запроса, именованную как access_token в запросе Socket.IO.
  4. Служба проверит access_token. Подключение будет отклонено, если access_token недействителен.

Веб-приложение, обрабатывающее запрос на согласование, может быть независимым или частью приложения Socket.IO.

Базовое использование

  • на стороне сервера
  1. Создание сервера Socket.IO, поддерживаемого службой

Необработанные строки подключения приводятся в этой статье только для демонстрационных целей. В рабочих средах всегда защищать ключи доступа. Используйте Azure Key Vault для безопасного управления и ротации ключей, а также защиты подключения.WebPubSubServiceClient

const azure = require("@azure/web-pubsub-socket.io");
const app = express();
const server = require('http').createServer(app);

const io = require('socket.io')(server);
const wpsOptions = { hub: "eio_hub", connectionString: process.env.WebPubSubConnectionString };

azure.useAzureSocketIO(io, wpsOptions);
  1. Используйте negotiate для преобразования сервера Socket.IO и ConfigureNegotiateOptions в полноценный обработчик Express.
app.get("/negotiate", azure.negotiate(io, (req) => { userId: "user1" });)
  • На стороне клиента
  1. Выполнение запроса на согласование и анализ результата
const negotiateResponse = await fetch(`/negotiate/?username=${username}`);
if (!negotiateResponse.ok) {
  console.log("Failed to negotiate, status code =", negotiateResponse.status);
  return ;
}
const json = await negotiateResponse.json();
  1. Клиент должен подключиться к точке подключения службы, используя информацию из ответа на обмен данными.
var socket = io(json.endpoint, {
  path: json.path,
  query: { access_token: json.token }
});

Полный пример представлен в chat-with-negotiate.

Интеграция с библиотекой Passport

Общие сведения

В экосистеме Node.js наиболее доминирующим рабочим процессом веб-проверки подлинности является express + express-session + passport. Ниже приведен список, объясняющий их роли:

  • express: серверная платформа
  • express-session: официальная библиотека управления сеансами, поддерживаемая командой Express.
  • passport: пакет проверки подлинности для express. Он фокусируется на проверке подлинности запросов и поддерживает более 500 стратегий проверки подлинности, включая локальную проверку подлинности (имя пользователя и пароль), OAuth (Google, GitHub, Facebook), JWT, OpenID и многое другое.
    • После успешной проверки подлинности passport предоставляет объект, описывающий прошедшего проверку подлинности пользователя. Этот объект назначается свойству user в переменной express request. К свойству можно получить доступ в последующем промежуточном ПО.

Использование

Мы предоставляем встроенный ConfigureNegotiateOptions через userPassport и промежуточное ПО через restorePassport для поддержки интеграции с passport.

Socket.IO содержит пример использования проверки подлинности паспорта с собственной библиотекой Socket.IO.

В этой части кода используется набор промежуточного ПО Socket.IO для восстановления объекта passport в запрос.

const io = require('socket.io')(server);

// convert a connect middleware to a Socket.IO middleware
const wrap = middleware => (socket, next) => middleware(socket.request, {}, next);

io.use(wrap(sessionMiddleware));
io.use(wrap(passport.initialize()));
io.use(wrap(passport.session()));

io.use((socket, next) => {
  if (socket.request.user) {
    next();
  } else {
    next(new Error('unauthorized'))
  }
});

После активации useAzureSocketIO службы разработчик должен добавить переговорный обработчик для приложения express. usePassport создает свой ConfigureNegotiateOptions. Затем модуль Express restorePassport должен использоваться в качестве промежуточного слоя Socket.IO для восстановления объекта passport в socket.request.

const io = require('socket.io')(server);

await useAzureSocketIO(io, { ...wpsOptions });

app.get("/negotiate", negotiate(io, usePassport()));

io.use(wrap(restorePassport()));

io.use(wrap(passport.initialize()));
io.use(wrap(passport.session()));

io.use((socket, next) => {
  if (socket.request.user) {
    next();
  } else {
    next(new Error('unauthorized'))
  }
});

Этот рабочий процесс не восстановит объект сеанса. Сеанс недоступен посредником Socket.IO. socket.request.session не работает, потому что это всегда null.

// This usage will NOT work
io.use((socket, next) => {
  var session = socket.request.session; 
  // ... some code uses `session`
});

// This usage will NOT work
io.on('connect', (socket) => {
  const session = socket.request.session;
  // ... some code uses `session`
});

Полный пример приведен в чате с использованием auth-passport.

Внимание

Неправильный порядок использования ПО промежуточного слоя может привести к сбою рабочего процесса проверки подлинности. Следуйте порядку, указанному в нашем примере, если вы не знаете механизм этих промежуточных уровней.