Дизайн для мобильной аутентификации с сервером NodeJS

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

Решение, которое я придумал, довольно сложное, и я не уверен на 100%, что все это является лучшей практикой, поэтому я подумал, что спрошу и получу любые предложения и указатели на то, что я могу исправить, что работает хорошо, что плохо и т. д. .

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

Для реализации аутентификации я использовал JWT (веб-токены JSON) вместо файлов cookie по целому ряду причин. 1) Они работают намного лучше с мобильными устройствами 2) не имеют сеансов, что значительно упрощает реализацию сервера, и, насколько мне известно, не подвержены атакам CORS. JWT кажется лучшим решением при работе с мобильными устройствами. Я использовал много библиотек npm, в первую очередь express-jwt и jsonwebtoken для аутентификации на стороне сервера.

Как я упоминал выше, я не только пытался выполнить аутентификацию, я также хочу разрешить пользователям регистрироваться в любой сторонней службе, которую они хотят, например, в Facebook, Twitter, чтобы уменьшить трения пользователей во время регистрации. Подумав об этом некоторое время и много погуглив, я пришел к идее поставщиков удостоверений, системы аутентификации, в которой каждый «тип учетной записи» рассматривается как отдельный поставщик удостоверений и обобщается для предоставления такой информации, как access_token. , user_id, данные об истечении срока действия и т. д. Поставщики удостоверений очень похожи на «связанные учетные записи», которые вы видите на многих страницах настроек приложений. Что касается iOS, я создал абстрактный класс, и для каждой службы, которую я хочу поддерживать, я создал конкретный подкласс, FacebookIdentityProvider, LocalIdentityProvider (электронная почта/пароль) и т. д.

На стороне сервера я использовал модули Passport для поддержки каждого поставщика удостоверений. Например, у них есть модуль facebook-token, один для электронной почты и паролей пользователя и т. д. Поэтому я создал один API-маршрут /authenticate, на который мои клиенты делают запрос с сериализованным поставщиком удостоверений и на основе строки идентификатора, local, facebook-token, паспорта. вызовет соответствующий подмодуль для аутентификации этого провайдера на основе предоставленной информации.

В целом, поток безопасности выглядит следующим образом:

  1. Клиент проверяет диск на наличие предыдущего токена JWT (надежно хранится с помощью Lockbox).
  2. Если токен найден, клиент делает запрос к моей конечной точке verify. Эта конечная точка проверит, действителен ли токен и не истек ли срок его действия.
  3. Если срок действия токена не истек, клиенту отправляется 200, и с миром все в порядке. Если нет, то клиент сделает запрос к моей конечной точке refresh_token с токеном с истекшим сроком действия, который попытается повторно выпустить токен. Если это не удается, то клиент делает запрос к моей конечной точке authenticate, который может быть вызван только в результате действия пользователя.
  4. Если токен изначально не найден на диске, происходит то же самое, что и в конце 3, клиент должен ждать аутентификации пользователя.

После того, как все это сделано и реализовано, я все еще немного не уверен в некоторых вещах. В первую очередь я читал что-то на странице express-jwt об отзыве токенов. Что определяет, когда я должен отозвать токен и снова войти в систему? Нет смысла обновлять их токен каждый раз, когда срок его действия истекает на неопределенный срок.

Во-вторых, когда я отправляю поставщика сериализованных удостоверений на сервер, я передаю словарь дополнительной информации, которая будет использоваться паспортом для аутентификации на основе процесса. В случае успеха для этого пользователя создается поставщик удостоверений, который сохраняется в базе данных. Этого достаточно, или я должен делать больше с access_token и другими полями, которые я получаю после успешного вызова? В частности, с Facebook SDK я получаю токен доступа, когда клиент проходит аутентификацию через приложение, а затем другой токен, когда клиент снова проходит аутентификацию на сервере.

Еще у меня была идея, чтобы кто-то интегрировал ключ API, который передавался с каждым запросом либо через заголовок, либо через параметр запроса. Ключ API будет храниться в секрете и защищен на стороне клиента. Я думаю, что это добавит еще один уровень «аутентификации» даже для клиентов, которые еще не прошли процесс аутентификации. Только клиенты с API-ключом смогут получить доступ к моему API, и только эти клиенты смогут попытаться пройти аутентификацию.

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

Также я чувствую, что должен упомянуть, что все это делается через SSL, который я настроил с помощью Nginx, и все мои сетевые запросы iOS выполняются с использованием Overcoat. В конце концов я хочу использовать Nginx в качестве балансировщика нагрузки, но это тема для другого дня.


person barndog    schedule 25.06.2015    source источник
comment
Это не место для StackOverflow.   -  person fulvio    schedule 30.10.2015
comment
Тогда где он принадлежит? Сказать, что это не принадлежит, бесполезно.   -  person barndog    schedule 30.10.2015
comment
Если это связано с концепциями программирования более высокого уровня или концептуально (но все же связано с программированием), оно должно быть на programmers.stackexchange.com   -  person fulvio    schedule 02.11.2015


Ответы (1)


Я просто недавно использовал что-то подобное. Отзыв неправильно понимается в JWT, вы просто не можете «отозвать» его до тех пор, пока клиент не запросит токен обновления, вы можете отклонить запросы на конкретный токен, но вы не можете удалить его с устройства клиента до истечения срока его действия, просто используя сервер покоя. Ну, в клиентском приложении вы, конечно, можете удалить токен, если получите отказ от сервера.

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

  • Вы можете хранить хешированный пароль в токене (в зашифрованном виде), и в каждом запросе вы можете проверить, совпадает ли хешированный пароль, хранящийся в токене, с записью вашего пользователя в вашей базе данных.

  • У вас может быть другая таблица в БД, содержащая идентификатор пользователя, токен, информацию об устройстве, отозванное поле (true/false). Вы просто отслеживаете отозванные токены в этой таблице, когда устройство запрашивает обновление с использованием отозванного токена по истечении срока его действия, вы просто возвращаете ошибку, а также можете удалить эту строку из таблицы, так как просроченные токены больше не проблема.

Надеюсь это поможет.

person Ahmet Cetin    schedule 06.01.2016