Русский
Русский
English
Статистика
Реклама

Блог компании мегафон

Авторизация и аутентификация на NodeJs и Socket.io и проблемы вокруг

19.01.2021 12:23:47 | Автор: admin

На текущий момент я работаю в компании МегаФон тимлидом фронта. С начала 2020 года мы в команде МегаФона разрабатываем собственную платформу Интернета вещей. Так как в таком процессе нагрузка на бэк-энд разработчиков стала колоссальной, а фронт не так активно задействован, внутри отдела было принято решение отдать всю веб-часть в руки моей команды. Очевидно, что мы взяли NodeJs с ExpressJS, и занялись построением серверной архитектуры.

Для корректного доступа к собранным с устройств данным нужна была авторизация, чтобы понимать, кто и что может получить/сделать. Про очевидный путь с passportJs, думаю, нет смысла рассказывать. По логину и паролю мы стали отдавать jwt токен, и изначально на этом успокоились.

После этого нам потребовалось хранить в сессии данные, специфичные для каждого пользователя. Логичным решением было бы использовать сам jwt токен, хранить информацию в нем и гонять от клиента к серверу. Однако, данное решение не подходило нам из-за использования веб-сокетов (в нашем случае мы взяли socket.io), так как в данном протоколе передача хедера Authorization с jwt токеном невозможна (в соответствии со стандартом). Единственный вариант - передавать хедер в параметрах url. Но это не очень здорово - токены будут легко видны во всех логах всех прокси-серверов. Хорошим решением оказалось использование сессии, которая хранится полностью на серверной стороне, и по сети ходит лишь id этой сессии. Мы выбрали - express-session.

Объединенная сессия

Отдельной проблемой стала необходимость получения актуального состояния сессии и возможность его изменения в событиях веб-сокетов. Для этого идеально подошел пакет - express-socket.io-session. Правда, пришлось поколдовать над её подключением:

Изменили подключение сессии и настройки кук:

this.store = new pgSession({          pool: pgPool,          tableName: SESSION_TABLE      });this.session = expressSession({    name: SESSION_KEY,    secret: SESSION.secret,    resave: false, // важно, для того, чтобы сессия не перезаписывалась на каждый чих    rolling: true,    saveUninitialized: true, // нужно для выдачи куки даже неавторизированному пользователю    proxy: true,    cookie: {        secure: true, // обязывает производить передачу по ssl        maxAge: SESSION_DURATION,        sameSite: 'none' // чтобы можно было отдавать на разные поддомены    }    store: this.store});

Мы написали обработчики сессии таким образом, чтобы сессия подгружалась до начала обработки события, и сохранялась, если необходимо:

const asyncHandlerExtended = (fn, socket) => (data) => {    const cb = async () => {        await reloadSession(socket.handshake.session);        await fn({ socket, data });        await saveSession(socket.handshake.session);    };    return Promise.resolve(cb()).catch((err) => {        socket.emit('error', err);    });};

Собрали все вместе при настройке сокетов:

import sharedSession from 'express-socket.io-session';import io from 'socket.io';const resultSocket = nameSpace ? this.io.of(nameSpace) : this.io;resultSocket.use(sharedSession(session, { autoSave: true }));

Разделение сокетов по ролям

Дальше нам нужно понимание того, кому и какие события можно получать на сокетах, а какие - нет. Для этого отлично подходит механизм комнат в socket.io. Он позволяет серверу формировать пространства, в которые можно "запускать" пользователей и эмитить в них разные события. Мы выделили под каждую из ролей пользователей отдельную комнату (например комната adminRoom - пространство для событий, которые могут идти/поступать только для администраторов), а общее пространство теперь у нас считается "публичным" и доступно для всех подключенных, но не авторизованных пользователей. Таким образом, процесс получения доступов на сокетах выглядит так:

  1. Клиент аутентифицируется по http, по паре логин/пароль, получает в ответ jwt токен и куку с id сессии.

  2. Далее юзер коннектится к нашей точке входа для socket.io (например: localhost:8080/sockets). Теперь у него есть доступ до публичных событий на наших сокетах.

  3. Если он хочет получить доступ до всех наших событий, которые ему доступны по роли, то он отправляет событие auth_login по сокетам, с jwt токеном, который он получил от http авторизации.

  4. Система проверяет токен и по результатам проверки генерирует одно из двух событий.

    1. auth_loginFailed - пользователю не будут предоставлены доступы, так как токен кривой или просрочен

    2. auth_loginSuccess - все хорошо, можно продолжать

  5. Если проверка прошла успешно, то сервер добавляет пользователя во все пространства предоставленные ему по его роли.

  6. Пользователю теперь доступны аутентифицированные и скрытые ранее события.

  7. Когда у токена проходит "срок годности", сокеты генерируют событие auth_expire, говорящее, что более пользователю недоступны ранее предоставленные комнаты.

Token steal

Вишенкой на торте в данной картине механизма авторизации/аутентификации стал результат изучения проблемы кражи токенов. Ради минимизации рисков от попадания в такую ситуацию, было решено улучшить механизмы работы с авторизационным токеном. Во время исследования данной темы наткнулся на статью на хабре - Зачем нужен Refresh Token, если есть Access Token?. Очень советую ознакомиться, но если кратко, то вот результирующая цитата:

Таким образом, схема refresh + access токен ограничивает время, на которое атакующий может получить доступ к сервису. По сравнению с одним токеном, которым злоумышленник может пользоваться неделями и никто об этом не узнает.

Однако, у нас уже есть два токена:

  • id серверной сессии от express-session, который ходит в куках, всегда

  • jwt токен, который генерируется после логина пользователя

Поэтому было бы логично реализовать похожий механизм, как и в OAuth2. Для этого мы стали хранить jwt токен в сессии и сравнивать его с полученным от клиента при проверке аутентификации. Остается только одна проблема - несколько токенов в одной сессии. Необходимо это для того, чтобы иметь возможность войти и в админку, и на фронт. Для этого считаем, что клиент передаст свое "название" при логине, и под этим названием мы и сохраним токен в сессию, а также записываем само название внутрь токена для дальнейшей проверки. За счет вышеописанного получается следующая схема взаимодействия:

  1. Клиент отправляет пару логин и пароль, плюс к этому уникальный ключ - название себя (то есть имя приложения).

  2. Система, если пара логин и пароль найдена, генерирует jwt токен, включая в него название клиента (ключ), отправляет его клиенту и записывает в сессию.

  3. При последующем обращении клиента по роуту, который закрыт проверкой аутентификации, проверяется наличие токена, его валидность, просрочен ли он. Далее сервер вытаскивает из токена название клиента и смотрит, есть ли такой токен с таким ключом в текущей сессии.

  4. Если вдруг токен и куку украл злоумышленник, то при первом же рефреше с любой из сторон (клиент или злоумышленник), сторона, которая попытается обратиться со старой парой токен + кука, получит негативный результат. Система поймет, что токен не соответствует тому, что хранит сессия и очистит сессию полностью, что привет к выбросу и клиента и злоумышленника, а факт кражи будет зафиксирован в системе.

  5. Если же все хорошо, то само собой мы отдадим данные =)

Таким образом, мы получили многосессионный механизм (один пользователь может зайти сразу с нескольких браузеров в одно приложение) аутентификации, который работает еще и несколько приложений сразу.

В заключение

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

Подробнее..

Навыки команды Как я настраивал матрицу компетенций

27.11.2020 12:11:35 | Автор: admin

Почему в Confluence?


Когда я пришел в команду разработки платформ бизнес-сервисов, мне было сложно разобраться, кто чем занимается, какие продукты разрабатываются и поддерживаются, какие навыки для этого нужны команде, кого и чему нужно научить.
В этой статье я расскажу, как мы строили матрицу навыков (компетенций) команды, как она эволюционировала, и какие вопросы мы теперь с её помощью решаем.
Статья будет полезна руководителям групп, тимлидам, которые хотят быстро внедрить процесс наполнения и актуализации матрицы компетенций.


Первое, что пришло на ум, это сделать таблицу, где в строчках сотрудники, а в колонках навыки/продукты. Встал вопрос, где вести такую таблицу?
  • Excel сложно организовать совместное редактирование
  • Google Docs не рекомендован к использованию в компании.

Подсказку сделать в Confluence я нашел в докладе Алексея Трошина (ФИНАМ) Знания и компетенции в команде: найти, увидеть, прокачать. Кому больше нравится читать держите конспект доклада от Светланы Новиковой.

Шаг 1. Обычная таблица


В начале я сделал обычную таблицу, где в строчках навыки, в колонках сотрудники. На тот момент в команде было 10 человек и с полсотни навыков, сгруппированных по тринадцати темам.
Начальный список навыков мы c командой придумали во время ретро. Таблица выглядела относительно компактно. И все с удовольствием пошли заполнять. Заполнять необходимо было количеством звездочек. Тут же возник вопрос? А сколько звездочек ставить? Нужны критерии.
Мы для себя выбрали такие (одна пять звезд):
  1. Давно изучал и давно не применял на практике
  2. Прослушал курс/изучал самостоятельно, практики было мало
  3. Практик, регулярно применяю на практике
  4. Эксперт, знаю тонкости, готов делится лайфхаками с коллегами
  5. Гуру, готов выступать на внешних конференциях




После этого все довольно оперативно смогли заполнить информацию по себе.


Итоги после первого шага:


Время: От идеи до первичного заполнения прошел один спринт (две недели).
Выхлоп: Стало понятно, где у нас пробелы в знаниях, кого чему нужно учить. Составили график обучения (заказали курсы в нужном объеме).

Шаг 2. Сводная таблица


Шло время, команда росла, добавление новой колонки с новым сотрудником становилось все труднее. Кто работал в Confluence, знает капризы работы с таблицами. В тому же появились новые навыки, захотелось их по-другому сгруппировать. Плюс оказалось, что анализировать 15 человек не так удобно, как десять. Поэтому я решил воспользоваться функционалом свойств страниц.
Для начала создал страницу Шаблон профиля специалиста.

Все навыки разделены по группам. Для каждой группы навыков добавил по отдельному свойству страницы.


Для примера на картинке выше две группы навыков: Computer languages и Management.

Далее добавил метку (Label) на страницу Шаблон профиля специалиста. Например, skills.


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

Теперь магия сводная таблица по свойствам страниц.


Добавил макрос Вертикальное меню, внутри него макрос Элемент вертикального меню, внутри него макрос Отчет по свойствам страницы.

У элемента вертикального списка задал название. Оно может быть любым и может не совпадать с названием свойства страницы.
А вот с макросом Отчет по свойствам страницы немного сложнее.
Во-первых, надо указать метку ту, что навесили на Шаблон профиля специалиста. Например, skills. Это позволило мне отобрать только нужные страницы.
Во-вторых, стоит ограничить список пространств, в которых будут искаться страницы. Например, текущим.
В-третьих, важно указать Свойство страницы, ведь, как мы помним [из предыдущего], в шаблоне сотрудника у нас много свойств, каждое из которых соответствует одной группе навыков. В данном случае computer language.
Наконец, лучше задать название первой колонке в сводной таблице. Я назвал её Сотрудник


Повторил для следующей группы навыков. Добавил Элемент вертикального меню и внутри него Отчет по свойствам страницы.
Теперь давайте посмотрим, что у меня получилось?


Итоги после второго шага:


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

Шаг 3. Продвинутый шаблон


Предупреждение: Для этого улучшения потребуются права администратора пространства.

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


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


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


Второе tooltip. Этот макрос позволил мне сделать всплывающее описание к навыку и будет полезен для просмотра информации на сводной таблице.


В итоге мой шаблон выглядит вот так:

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

Ой, я чуть не забыл рассказать про то, как воспользоваться нашим пользовательским шаблоном. Все просто. Добавляем макрос Создать из шаблона


В результате появляется кнопка<img

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


Я использую эту возможность для формирования отдельных сводных отчетов по мини-командам. Как вы помните, когда я пришел, нас было десять, на первой итерации изменений уже 15, сейчас нас более двадцати, и мы приняли решение делиться на четыре мини-команды.
В нашем случае это команды:
  • DEV внутренняя автоматизация
  • CI/CD развитие и эксплуатация таких продуктов как Artifactory, Gitlab, Jenkins и т.д.
  • k8s развитие kubernetes
  • TESTZONE автоматизация пересоздания тестовых зон

Кроме того, мы решили добавить в каждую команду по профилю целевого сотрудника. То есть что должны уметь делать все сотрудники внутри мини-команды для выполнения задач. Теперь каждый может легко сравнить себя с целевым и понять, что нужно прокачать. А дальше сотрудник идет на сборник ресурсов для саморазвития и выбирает нужные курсы / материалы.

Итоги после третьего шага:


Время: На разработку и тестирование нового шаблона у меня ушло две недели. Основное время на уточнение формулировок критериев для каждого навыка.
Выхлоп: Оценка уровня навыка стала более точной. Есть возможность сравнивать с целевым специалистом и планировать обучение.

ЗАКЛЮЧЕНИЕ


Вот какие задачи решает матрица компетенций у нас:
  • Во-первых, bus factor. Очень не хочется оказаться в ситуации, когда о том, как работает продукт, знает только один человек, и он уходит. Это полезно и для планирования, и для согласования отпусков.
  • В-вторых, нужно планировать платное обучение. Заранее принять и согласовать решение о том, кого, когда и на какие курсы отправить.
  • Во-третьих, самообучение. Есть как внутренние курсы, так и множество открытых ресурсов. Главное определить, какой навык нужно прокачивать и создать на это задачу.
  • И, наконец, мы нашли еще одно применение профиля целевого сотрудника: с его помощью мы решаем вопрос подбора персонала. Можем четко объяснить HR, кого мы ищем.

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

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru