Пускать или не пускать? Вот в чем вопрос…

Сейчас на многих сайтах мы видим возможность зарегистрироваться или войти с помощью соцсетей, а некоторые сайты предлагают использовать внешние ключи безопасности или отпечатки пальцев. Что это? Стандарты с хорошо проработанной безопасностью или проприетарные реализации? Можем ли мы доверять этим технологиям и использовать их для разработки сайтов и в повседневной жизни? Давайте разбираться. Итак, сейчас существуют несколько стандартов и технологий для идентификации пользователей OAuth 2.0,OpenID Connect, WebAuthn, SAML 2.0, Credential Management API и др. В статье я расскажу о трех наиболее перспективных протоколах OAuth 2.0, OpenID Connect и WebAuthn. И чтобы понять как их применять на практике, сделаем три лабораторные работы. В качестве платформ для идентификации пользователей будем использовать GitHub и Google, на которых у большинства есть аккаунты.

image

OAuth 2.0


Начнем с самого известного протокола OAuth 2.0. Он был опубликован в 2012 году, как RFC 6749: The OAuth 2.0 Authorization Framework.

С помощью OAuth 2.0 пользователь разрешает определенному сайту получить свои закрытые данные из соцсетей, но без передачи сайту своих логинов / паролей. Например, когда вы регистрируетесь на сайте через Facebook, то как раз и предоставляете этому сайту разрешение получить из Facebook ваше имя, e-mail адрес и другие закрытые данные.

Давайте разберемся с технической реализацией. Для простоты я буду называть любой сайт, который хранит идентификационные данные пользователя Соцсетью. А MySite буду называть любой сайт или приложение, которое хочет получить данные пользователя из Соцсети.

image

Стандарт определяет следующие роли:

  • Resource Owner — пользователь, который заходит на MySite и дает ему разрешение использовать свои закрытые данные из Соцсети.
  • Client (он же MySite) — приложение или интернет сайт, которым пользуется пользователь и которое взаимодействует с Authorization Server и Resource Server для получения закрытых данных пользователя.
  • Authorization Server — сервер который проверяет логин/пароль пользователя, он же Соцсеть.
  • Resource Server — хранит закрытую пользовательскую информацию, которую можно получить с помощью API. Authorization Server и Resource Server могут быть совмещены в одну систему.

Authorization flow


  • Перед началом авторизации необходимо зарегистрировать MySite в Соцсети:
  • Разработчик MySite задает Name (имя приложения), Homepage (адрес домашней страницы MySite) и Callback (адрес, на который Соцсеть перенаправит пользователя после успешной авторизации)
  • Соцсеть выдает Client ID (иногда его называют AppID) и Client Secret.
  • Client ID и Client Secret разработчик должен прописать в своем Client.

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

image

  1. Resource Owner заходит на Client (MySite), выбирает опцию “войти с помощью Соцсети”, сайт перенаправляет пользователя в Cоцсеть на Authorization Server.
  2. Authorization Server проверяет есть ли у пользователя активная сессия и, если нет, то показывает форму для логина.
  3. Resource Owner вводит свои логин/пароль и подтверждает, что определенные закрытые данные могут быть использованы MySite, например имя пользователя или e-mail адрес.
  4. Authorization Server проверяет пользователя и перенаправляет на адрес Callback с результатом аутентификации и “Authorization Code”
  5. В ответ Client посылает “Authorization Code”, Client ID и Client Secret.
  6. Authorization Server проверяет присланные данные и формирует “access token” в формате JWT (JSON Web Token), подписанный своим приватным ключем. В этом же JWT может содержаться и “refresh token”, c помощью которого возможно восстановление сессии после ее окончания.
  7. После этого Client может запросить закрытую информацию пользователя с помощью вызова API, в который передается “access token”.
  8. Resource Server проверяет “access token” (например, используя открытый ключ Authorization Server) и предоставляет доступ к данным пользователя.

OAuth 2.0 Лабораторная работа (GitHub)


Существует множество инструкций, как реализовать OAuth 2.0 авторизацию, используя соцсети. Мне лично понравилась следующая статья: Authentication using GitHub OAuth 2.0 with NodeJS В ней подробно описаны шаги и приведена тестовая программа. Но, чтобы действительно осознать алгоритм, лучше пройти все шаги руками (http запросами из браузера или вызовами curl). Поехали.

Для начала регистрируем свое приложение на GitHub: github.com/settings/applications/new

Задаем параметры:


Чтобы авторизация работала в рамках собственного сайта адреса должны быть реальными, но для лабораторной работы это не обязательно.

Получаем от GitHub:

  • Client ID: ab8ec08a620c2
  • Client Secret: e6fdd52b0a99e8fbe76b05c1b7bb93c1e

Разумеется во всех лабораторных работах все значения фейковые.

Так выглядит получение Client ID и Secret на сайте GitHub:

image

Теперь начинаем авторизацию. Считаем, что Шаг 1 уже пройден: пользователь зашел на MySite и выбрал “Войти с помощью GitHub ”. Шаг 2 из call flow — это вызов в REST формате:

https://github.com/login/oauth/authorize?client_id=ab8ec08a620c2


  • адрес — это точка логина на GitHub
  • client_id — это Client ID, выданный при регистрации

В результате этого вызова GitHub показывает окно для авторизации:

image

Шаг 3: Вводим логин/пароль для доступа на GitHub

Шаг 4: GitHub перенаправляет запрос на Homepage, в этом запросе мы видим code:

http://MySite/home?code=a29b348f63d21

По данному адресу нет работающего сайта, но для нас главное знать присланный code, чтобы сформировать следующий Шаг 5:

https://github.com/login/oauth/access_token?client_id=ab8ec08a620c2&
client_secret=e6fdd52b0a99e8fbe76b05c1b7bb93c1e&
code=a29b348f63d21

  • адрес — это точка получения access token на GitHub
  • client_id — это Client ID, выданный при регистрации
  • client_secret это Client Secret, выданный при регистрации
  • code — это только что присланный code

Шаг 6: В ответ получили access token:

access_token=31b71cbd372acdbb20ec1644b824f3dd0&scope=&token_type=bearer

Шаг 7: Вставляем access_token в заголовок запроса и вызоваем GitHub API:

curl -H "Authorization: token 31b71cbd372acdbb20ec1644b824f3dd0" https://api.github.com/user

Шаг 8: В ответ получаем JSON с полезной информацией обо мне, которую можно использовать для формирования профиля на MySite:

{
  "login": "AlexeySushkov",
  "html_url": "https://github.com/AlexeySushkov",
  "repos_url": "https://api.github.com/users/AlexeySushkov/repos",
  "name": "Alexey Sushkov",
  "blog": "http://sushkov.ru",
  "location": "St.Petersburg, Russia",
  "email": "alexey.p.sushkov@gmail.com",
и т.д.
}  

На самой деле, мы рассмотрели только один сценарий работы OAuth 2.0. Существует несколько сценариев, каждый из которых используется в зависимости от приложения, соображений безопасности, способу развертывания и т.д. Описание всех сценариев можно найти, например, тут: OAuth 2.0 in a Nutshell.

OpenID Connect


С OAuth 2.0 немного разобрались. Теперь выясним зачем нужен OpenID Connect, который является надстройкой над OAuth 2.0:

  • C помощью OAuth 2.0 мы только авторизовали пользователя т.е. о пользователе мы знаем только access token, с помощью которого можем получать определенные данные из Соцсети. Но access token ничего не говорит о личности пользователя и с помощью него мы не можем предоставить доступ к закрытым данным пользователя на нашем сайте MySite. OpenID Connect — добавляет сведения о логине и профиле пользователя (identity). Это как раз и позволяет реализовать его аутентификацию.
  • OpenID Connect также добавляет возможность динамической регистрации и обнаружения сервисов “service discovery”. Это дает возможность строить системы SSO (Single Sign-On), в которых используется один логин для многих не связанных между собой сервисов.

Давайте посмотрим на стандарт с технической стороны.

OpenID Connect (OIDC) — открытый стандарта OpenID, который разрабатывает консорциум OpenID Foundation. OIDC расширяет OAuth 2.0 следующими основными возможностями:

  • Authorization Server, помимо access token и refresh token, возвращает “identity token” (ID Token). Он содержится в том же JWT. Из ID Token можно извлечь следующую информацию: имя пользователя, время входа в учетную запись, срок окончания действия ID Token. ID Token можно передавать между участниками.
  • OIDC предоставляет дополнительное API, которые позволяет запрашивать информацию о пользователе и его текущих сессиях.

Диаграмма взаимодействия в OpenID Connect выглядит так же, как и в случае OAuth. Единственная разница в содержимом запросов:

  • В первичном запросе на получение code добавляется дополнительный атрибут scope=openid.
  • В результате работы алгоритма Client, помимо access и refresh token, получает ID Token.

OpenID Connect: Лабораторная работа (Google)


Теперь посмотрим, чем нас по данной теме порадует Google. Есть подробная инструкция по конфигурации и использованию OpenID Connect от Google и “песочница” по использованию Google API: Google OAuth 2.0 Playground.

Здесь, как и в случае с OAuth 2.0, мы пройдем путь по шагам и посмотрим на приходящие данные. Аналогично считаем, что приложение зарегистрировано, Client ID и Client Secret получены, Шаг 1 пройден. Шаг 2 из call flow — это вызов в REST формате:

https://accounts.google.com/o/oauth2/v2/auth?
response_type=code&
client_id=140797064495-b8b79j42m97nkkrlndfstikv8.apps.googleusercontent.com&
scope=openid%20email&
redirect_uri=http%3A//c18529.shared.hc.ru/wp-login.php&
state=765439764

У Google немного посложнее, т.к. безопасности они уделяют больше внимания:

  • адрес — это точка логина на Google
  • response_type=code — ожидаем получить в ответ code
  • client_id — Client ID, выданный при регистрации
  • scope=openid email — к каким данным мы хотим получить доступ
  • redirect_uri — redirect_uri заданный при регистрации приложения
  • state — сгенерированное Client-ом число, которое передается между Client и AS для защиты от вмешательства злоумышленника.

Шаг 3: Форма ввода пароля отсутствует, т.к. я уже был залогинен в Google.

Шаг 4: Google перенаправляет запрос на Homepage, в этом запросе мы видим code:

http://MySite?state=123&code=4/xAFkcMzhyJhUErRJYwIyntSYN-WeJjfZHLiwWL4IaT-WkHzMU18xABlPmev-M_87wVbqTkQ1y93w6GB5&scope=email+openid+https://www.googleapis.com/auth/userinfo.email&authuser=0&prompt=none

Также как и в случае с GitHub по данному адресу нет работающего сайта, но это и не надо, нам главное знать code, чтобы сформировать следующий Шаг 5. Тут тоже немного посложнее, т.к. Google требует запрос POST, а не GET:

curl -d "code=4/xAFkcMzhyJhUErRJYwIyntSYN-WeJjfZHLiwWL4IaT-WkHzMU18xABlPmev-M_87wVbqTkQ1y93w6GB5&client_id=140797064495-b8b79j42m97nkkrlndfstikv8.apps.googleusercontent.com&
client_secret=HMVctrTicW6RC1Q8T&
redirect_uri=http%3A//c18529.shared.hc.ru/wp-login.php&
grant_type=authorization_code" 
-H "Content-Type: application/x-www-form-urlencoded" -X POST https://oauth2.googleapis.com/token

  • адрес — это точка получения token на Google
  • code — это только что присланный code
  • client_id — это Client ID, выданный при регистрации
  • client_secret это Client Secret, выданный при регистрации
  • grant_type=authorization_code — единственно правильное значение из стандарта

Шаг 6: В ответ получили access_token и id_token:

{
  "access_token": "ya29.Il_AB0KeKnjBJx0dhjm2nCLt1B-Mq0aQBW5T302JnlZfsxW1AXqLFfDJZRMi2R2WKG4OX4msKLjx4E4CSl4G_4ajAy3aqrf4pM0ic0jJr092pA67H9aktJktCbx",
  "expires_in": 3327,
  "scope": "openid https://www.googleapis.com/auth/userinfo.email",
  "token_type": "Bearer",
  "id_token": "eyJhbGciOiJSUzI1NiIsImtpZCI6IjE3ZDU1ZmY0ZTEwOTkxZDZiMGVmZDM5MmI5MWEzM2U1
………………………………много_букв…………………………………………………….._4mUTiMNSAHljap1hLD2hAzgOZWuQ"
}

Что теперь делать с этим богатством?

Шаг 7: C access_token все понятно: включаем его в вызов API, например GMail:

curl -H "Authorization: Bearer ya29.a0Adw1xeWvFoxHKNICHnV6vFFj5TZdPQVlYD98h8wjW95ZEbHVui_pk7HGRoq3Q7MlVLV23xkVM0yyjSP8ClSlvfUy3b_IqvKQW5Lvwj38QzJhee-aH1grerB4pRpMzn_FGueigG_RGI56pKPgFBTr49cpynQy" https://www.googleapis.com/gmail/v1/users/alexey.p.sushkov@gmail.com/profile

Шаг 8: В ответ получаем JSON с полезной информацией:

{
 "emailAddress": "alexey.p.sushkov@gmail.com",
 "messagesTotal": 372543,
...
}

Теперь давайте проверим утверждение, что в id_token содержится информация для аутентификации пользователя и поддержания сессии. Для этого надо расшифровать содержимое. Самый простой способ сделать это, обратиться к Google API по адресу
oauth2.googleapis.com/tokeninfo и в качестве параметра указать полученный id_token:

https://oauth2.googleapis.com/tokeninfo?id_token=eyJhbGciOiJSUzI1NiIsImtpZCI6IjE3ZDU1ZmY0ZTEwOTkxZDZiMGVmZDM5MmI5MWEzM2U1NGMwZTIxOGIiLCJ0eXAiOi
………………………много_букв……………………………...
SVQ5GQni3irfOzOXYEiqijp6TjGa_a-3jKcEsU5TbasZmAIejsdVcNy2_4mUTiMNSAHljap1hLD2hAzgOZWuQ

Получили JSON:
{
  "iss": "https://accounts.google.com",
  "email": "alexey.p.sushkov@gmail.com",
  "email_verified": "true",
  "iat": "1583257010",
  "exp": "1583260610",
  "typ": "JWT"
...
}

Видим, что в id_token содержится информация о логине пользователя, времени получения и времени жизни токена. Можно сделать вывод, что OpenID Connect от Google работает, и его можно применять для соответствующих сценариев.

WebAuthn


Web Authentication API (also known as WebAuthn):

  • Стандарт дает пользователям возможность идентифицироваться на сайтах и в приложениях с помощью внешних ключей безопасности (например, USB-ключей) или по отпечатку пальца и, впоследствии, по другим биометрическим данным: лицу, сетчатке глаза.
  • Основная задача стандарта — избавить пользователей от логинов / паролей и перейти на идентификацию с помощью криптографии с использованием публичных ключей «Public key cryptography». Public key cryptography — это криптографическая концепция, использующая пары математически связанных ключей. Private key (закрытый ключ) хранится в безопасном месте у пользователя, а public key (открытый ключ) хранится и используется открыто.
  • Над стандартом работют организации W3C (World Wide Web Consortium) и FIDO, при участии Google, Mozilla, Microsoft, Yubico. W3C дал миру стандарты HTTP, HTML, XML и многие другие. Наличие таких серьезных игроков говорит о большом потенциале стандарта WebAuthn. WebAuthn поддерживается в следующих браузерах: Chrome, Firefox, Edge, Safari.

Роли


По сравнению с OAuth 2.0 в WebAuthn добавляются роли:

Authenticator: внешний ключ безопасности (физический носитель или сканер отпечатков пальцев), который позволяет аутентифицировать пользователя, использую различные технологии, такие как BlueTooth/NFC/USB. Служит для:

  • Генерации public key credentials (пар открытых / закрытых ключей).
  • Authenticator безопасно хранит закрытый ключ в своей памяти
  • Передает открытый ключ внешним системам
  • Подписывает данные закрытым ключом и результат передает внешним системам

Для взаимодействия с браузером Authenticator использует протокол CTAP (Client to Authenticator Protocols).

Relying Party: выполняет ту же функцию, что и “Authorization Server” в OAuth 2.0, а именно проверяет идентификационные данные пользователя. Только в OAuth 2.0 это были логин/пароль, а в WenAuthn — public key credentials.

User Agent: объединяет браузер и сетевое приложение, служит для тех же целей, что и Client в OAuth 2.0, а именно — с одной стороны взаимодействует с пользователем и предоставляет ему GUI, а с другой стороны взаимодействует с системами, в которых хранятся идентификационные данные пользователя.

Authorization flow


Перед началом процесса аутентификации, также как и в OAuth 2.0, надо совершить подготовительные действия, только в OAuth 2.0 мы регистрировали приложение, а в WebAuth 2.0 регистрируем пользователя. В Web Authentication API специфицировано два вызова:

  • navigator.credentials.create — для создания идентификационных данных пользователя
  • navigator.credentials.get — осуществляет проверку идентификационных данных пользователя

Соответственно, для регистрации надо вызвать navigator.credentials.create.

В результате этого в Authenticator-е пользователя сохранится закрытый ключ для определенного сайта, а в Relying Party сохранится открытый ключ.

image

После этого процесс аутентификации будет таким:

image

  1. Зарегистрированный пользователь заходит на сайт и в форме логина выбирает войти с помощью “WebAuthn”. На сайте будет написано, конечно, не WebAuthn, а “войти по отпечатку пальца” или “войти с помощью внешнего ключа”. Приложение делает на Relying Party запрос Challenge. Challenge — имеет тот же смысл, что и Code в OAuth 2.0.
  2. Relying Party присылает Challenge. Этот обмен не регламентируется стандартом, но обычно используется формат REST API.
  3. Взаимодействие браузера и Authenticator-а происходит по протоколу CTAP (Client to Authenticator Protocols). Браузер вызывает navigator.credentials.get c параметрами:

    • Challenge
    • Дополнительные атрибуты
  4. Authenticator ищет идентификационные данные пользователя, опционально запрашивает у пользователя подтвердить, что он это он.
  5. Если проверка прошла успешно, то Authenticator возвращает подписанные приватным ключом данные.
  6. Приложение посылает подписанные данные на Relying Party.
  7. Relying Party расшифровывает данные с помощью открытого ключа, проверяет Challenge и авторизует пользователя.

Для закрепления материала делаем лабораторную работу:

WebAuthn: Лабораторная работа (Google)


Для реализации WebAuthn только http запросами не обойтись, т.к. нужно вызывать браузерное API для взаимодействия с Authenticator-ом. Но и тут Google радует, сделали песочницу с пошаговой инструкцией: Your first WebAuthn.

В результате выполнения работы получится JS клиент-серверное приложение, которое реализует аутентификацию с помощью отпечатка пальца. Работающая дема находится по адресу.

Если ее запустить на смартфоне с датчиком отпечатков пальцев, то можно посмотреть результат работы. Как обычно, сначала подготовка — регистрируем пользователя:

image

Создаем логин / пароль и далее привязываем отпечаток пальца:

image

После этого программа показывает какой открытый ключ привязан к этому отпечатку:

image

Теперь можно начинать сценарий по аутентификации. Как обычно, считаем, что Шаг 1 пройден и мы находимся на сайте. Чтобы перейти к Шагу 2 надо нажать “Try Reauth”. Браузер выполнит Шаг 3 и вступит во взаимодействие с Authenticator-ом, который на Шаге 4 попросит приложить палец:

image

И если приложен зарегистрированный палец, то Шаги 5 и 6 успешно пройдут и на Шаге 7 приложение опять покажет окно с соответствующим открытым ключом:

image

Заключение


Итак, мы рассмотрели три наиболее распространенных и перспективных протокола для идентификации пользователей: OAuth 2.0, OpenID Connect, WebAuthn. Поняли области их применимости:

  • OAuth 2.0 — используется для регистрации и входа пользователей на сайты с помощью соцсетей. А также для получения данных пользователей из соцсетей.
  • OpenID Connect — используется для аутентификации пользователей и позволят предоставить им доступ к своим закрытым данным на сайтах. Также OpenID Connect служит для реализации сложных сценариев взаимодействия в корпоративных SSO системах.
  • WebAuthn — используется для добавления на сайт возможности аутентификации с помощью внешнего физического ключа или отпечатка пальца.

Выводы


  • Очевидно, что современный сайт должен реализовать все возможные способы регистрации и авторизации, чтобы у пользователя был осознанный выбор.
  • Появилась возможность никогда не хранить пароль пользователя, чтобы не компрометировать свою репутацию, а хранить только логины и зашифрованные личные данные.
  • Имеет смысл отдать аутентификацию пользователей облачным платформам, таким как Facebook или Google, т.к. в них работают лучшие специалисты по безопасности, которые могут предусмотреть все нюансы безопасности.
  • Предлагаю с оптимизмом смотреть в будущее, т.к. протокол WebAuthn — реальный шанс избавится от парольного ада нашего времени!

It's only the beginning!

Приложение: остальные протоколы идентификации


Для полноты картины перечислю остальные актуальные протоколы и технологии, используемые для идентификации пользователей:

SAML 2.0 (Security Assertion Markup Language)


Зрелый протокол 2005 года, но имеет ограниченный набор сценариев для построения систем SSO. Использует формат данных на основе XML. Подробнее можно посмотреть в статье: “Кто использует протокол аутентификации SAML 2.0”

Credential Management API


Разработкой занимается та же организация, что и WebAuthn — W3C. Стандарт Credential Management позволяет:

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

Пример реализации Credential Management API, известный всем — это диспетчер паролей в Google: passwords.google.com

Initiative for Open Authentication (OATH)


OATH Resources

Полный список протоколов, построенных на базе OAuth 2.0