@hbrmdc

Как правильно реализовать аутентификацию в SPA на Socket.io?

Socket.io, Express, PostgreSQL.
Веб-приложение доступно для просмотра как авторизованным пользователям, так и гостям. Все общение с сервером исключительно через wss. Просмотрев несколько статей о реализации аутентификации, я пришел к следующему.
Гость входит на сайт, происходит handshake, устанавливается соединение с сервером. Так как это не restful http соединение, а ws, то sid (session id) неавторизованным пользователям не нужен. Пока sidа нет, гостю доступен только ограниченный ряд функций (запросов в бд).
Гость вводит в форму авторизации логин и пароль, и жмет "войти". Пара логин-пароль отправляются по https. Так как имеется SSL сертификат, пароль отправляется без криптования. На сервере пароль криптуется SHA256, после чего происходит поиск по бд пары логин-криптованный_пароль соответствующий введенным данным. Если найдено соответствие, то генерируется sid (guid) и одноразовый ключ для ws (назовем его wskey). Оба ключа добавляются в в таблицу 'users' базы данных. Оба ключа отправляются клиенту. На клиенте sid сохраняется в куки с меткой HttpOnly, а wskey сразу же используется для отправки по ws. Серверный socket.io ищет в бд пользователя с этим wskey. Если нашел - то socket.io получает sid и как-то привязывает его к текущему соединению чтобы далее он прикреплялся ко всем запросам клиента к серверу.(1) Как это сделать? Как только sid сохранен в socket.io, wskey удаляется из бд.
То есть дальше клиент делает запросы без sidа или чего-то подобного, но socket.io его "помнит" и прикрепляет к каждому запросу sid, после чего отправляет его на дальнейшую обработку.
Пользователь сделал что хотел и закрыл браузер.

Пользователь снова открыл браузер и зашел на сайт.
Происходит HTTPS запрос, содержащий sid из куков. Сервер получил sid, нашел в бд в таблице пользователей строку с этим sidом и пошел по той же схемме - создать wskey, отправить клиенту и тд.

2. Если есть какие-то ошибки в написанном - пожалуйста, раскритикуйте)
  • Вопрос задан
  • 898 просмотров
Решения вопроса 1
Fesor
@Fesor
Full-stack developer (Symfony, Angular)
пароль отправляется без криптования

без SSL пининга можно и покриптовать пароль, но это нужно далеко не всем.

На сервере пароль криптуется SHA256

не криптуется а хэшируется, и лучше хэшировать в BCRYPT (или SCRYPT даже но я бы еще подождал пару годиков)

после чего происходит поиск по бд пары логин-криптованный_пароль соответствующий введенным данным

Ммм... шансы конечно малы... я бы даже сказал мизерные, но мы таким образом становимся уязвимыми к тайминг атакам. На данный момент адекватный алгоритм - это поиск по логину (идентити, логин или email или что вы там используете для идентификации пользователя) а затем сверяем хэши паролей посимвольно:

var matches = true;
for(var i = 0; i < 1024; i++) {
    if (str1[i] !== str2[i]) matches = false;
}


почему так убого? потому что почитайте про тайминг атаки. Есть даже задокументированный случай с эксплуатацией тайминг атак для подбора коллизий к хэшам через интернет (то есть с задержками порядка 10 и более милисекунд)! Правда для этого надо сделать пару миллионов запросов ну да несуть.

Серверный socket.io ищет в бд пользователя с этим wskey

Еще хорошая идея - socker.io серверный спрашивает у сервера авторизации правильный wskey или нет но это если вы упарываетесь по масштабированию.

Если есть какие-то ошибки в написанном - пожалуйста, раскритикуйте)

В целом все верно. А если не загоняться такими вещами как разделение ответственности (мол у вас отдельно socket-io сервер и отдельно приложение) то можно еще и JWT юзать. Тогда нам вообще не надо ничего нигде хранить а проверять валидность можно по подписи токена. Да и реализация готовая есть.
Ответ написан
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы