Требования аутентификации и авторизации API | learnapidoc-ru

Hmac (код авторизации сообщений на основе хэша)

HMAC расшифровывается как Hash-based message authorization code – код авторизации сообщений на основе хэша и является более строгим типом аутентификации, более распространенным в финансовых API. В HMAC только отправитель, и получатель знают секретный ключ, который больше неизвестен никому.

Затем сообщение кодируется секретным ключом и проходит через алгоритм безопасного хеширования (SHA – secure hashing algorithm). (Хеш – это зашифрованная строка на основе алгоритма.) Результирующее значение, называемое сигнатурой, помещается в заголовок запроса.

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

Вот диаграмма, отображающая процесс авторизации HMAC:

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

Что документируется в разделе аутентификации

В документации API не нужно подробно объяснять внешним пользователям, как работает аутентификация. Отсутствие объяснений внутренних процессов аутентификации, является лучшей практикой, поскольку хакерам будет сложнее злоупотреблять API.

Тем не менее нужно объяснить необходимую информацию:

  • как получить API ключ;
  • как пройти аутентификацию запроса;
  • сообщения об ошибках, связанных с неверной аутентификацией;
  • чувствительность информации аутентификации;
  • период действия токена доступа (авторизации).

Если есть открытый и закрытый ключи, нужно объяснить, где следует использовать каждый ключ, и отметить, что закрытые ключи не должны использоваться совместно. Если разные уровни лицензий предоставляют разный доступ к вызовам API, эти уровни лицензирования должны быть явно указаны в разделе авторизации или в другом месте.

Похожее:  Авторизация и аутентификация для всех и каждого | Techrocks

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

1 Время восстановления/энтропия входных параметров

Авторизация принимает решение о разграничении доступа на основе множества входных параметров. Если таковых будет слишком много, либо мерность множеств окажется слишком большой, то авторизация будет терять слишком много ресурсов на выполнение ненужных операций. Это как сортировать данные пузырьком и qsort. Не следует выполнять ненужные операции.

Если у вашей системы есть на входе A,B,C,D,E,F — факты о пользователе, то если вы будете объединять все в одно, то получите A * B * C * D * E * F комбинаций, которые невозможно эффективно кешировать. По возможности следует найти такую комбинацию входных, что бы у вас было A * B * C и D * E * F комбинаций, а еще лучше A * B, C * D, E * F, которые вы уже легко сможете вычислять и кешировать.

Эта характеристика очень важна так же для построения системы правил, которые будут предоставлять пользователям доступы.

10 О кешировании данных

Основной проблемой при работе авторизации является точка в которой необходимо кешировать результаты, что бы работало быстрее все, да еще желательно так, что бы нагрузка на ЦПУ не была высокой. Вы можете поместить все ваши данные в память и спокойно их читать, но это дорогое решение и не все готовы будут себе его позволить, особенно если требуется всего лишь пара ТБ ОЗУ.

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

Предлагаю решить такую задачу на примере ролей пользователя и некоего микросервиса, которому нужно проверять наличие роли у пользователя. Разумеется в данном случае можно сделать карту (Пользователь, Роль) -> Boolean. Проблема в том, что все равно придется на каждую пару делать запрос на удаленный сервис.

Даже если вам данные будут предоставлять за 0,1 мс, то ваш код будет работать все равно медленно. Очевидным решением будет кешировать сразу роли пользователя! В итоге у нас будет кеш Пользователь -> Роль[]. При таком подходе на какое-то время микросервис сможет обрабатывать запросы от пользователя без необходимости нагружать другие микросервисы.

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

Полагаю, что ход мыслей, которому стоит следовать, стал понятен.

5 Роль

Множество разрешений, по сути под словом роль всегда подразумевается множество разрешений.

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

Ролевые модели позволяют как выполнять вертикальное, так и горизонтальное разграничение доступа (описание ниже). Из типа разграничения доступа следует, что сами роли делятся на типы:

  • Глобальные — роли применимые ко всем сервисам — для вертикального разграничения;
  • Локальные — применяются только к ресурсу — горизонтальное разграничение.

Но отсюда следует вопрос, если у пользователя есть глобальная роль и локальная, то как определить его эффективные разрешения? Поэтому авторизация должна быть в виде одной из форм:

  • Конъюнктивная
  • Дизъюнктивная


Подробное описание использования типа ролей и формы авторизации ниже.

Роль имеет следующие связи:

  • Пользователь — у каждого пользователя может быть множество ролей;
  • Разрешение — каждая роль объединяет в себе несколько разрешений всегда вне зависимости от типа ролей.

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

7 Вертикальное и горизонтальное разграничение доступа.

Самым простым способом представить себе работу авторизации — это нарисовать таблицы на каждого пользователя и каждый сервис, где колонки будут задавать разрешения, а строки — ресурсы, на пересечении ставятся галочки там, где доступ разрешен (привет excel-warrior’ам).

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

2 Логистика

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

Для реализации такого функционала нам потребуется реестр регионов и магазинов в качестве отдельного микросервиса, назовем его С1. Заявки и историю будем хранить на С2. Авторизация — А.Далее при обращении продавца для получения списка его магазинов (а у него может быть их несколько)

, С1 вернет только те, в которых у него есть меппинг (Пользователь, Магазин), так как ни в какие регионы он не добавлен и для продавца регионы всегда пустое множество. Разумеется при условии, что у пользователя есть разрешение просматривать список магазинов — посредством микросервиса А.

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

3 Секретные части документа

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

Когда пользователь обращается в сервис для получения блоков документа, микросервис в авторизации проверяет роль пользователя на чтение документов, далее в БД каждый блок имеет флаг — секрет. У документа есть меппинг (Пользователь, Документ) — список пользователей, которые подписали соглашение.

4 Команда и ее проекты

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

Такую проблему проще всего решить при помощи групп.Группа будет объединять все необходимые проекты в одно целое. При добавлении участника в команду, добавляем меппинг (Пользователь, Группа, Роль).

Допустим, что Вася — разработчик и ему нужно вызвать метод develop на микросервисе для выполнения своих обязанностей. Этот метод потребует у Васи роли в проекте — Разработчик.Как мы уже договаривались — регистрация пользователя в каждый проект для нас недопустима.

Поэтому микросервис проектов обратится в микросервис групп, что бы получить список групп, в которых Вася — Разработчик. Получив список этих групп можно легко проверить уже в БД микросервиса проектов — есть ли у проекта какая-нибудь из полученных групп и на основании этого предоставлять или запрещать ему доступ.

2 Архитектура решения

Для решения поставленной задачи нам необходимы следующие микросервисы:

  1. Ролевая модель для глобальной авторизации запросов
  2. Чайный сервис для предоставления возможности пить чай и получать статистику по выпитому
  3. Кракен — гейт с проверкой доступа к точке, все остальные проверки совершит чайный сервис


Графически это будет выглядеть так:

Требования аутентификации и авторизации API | learnapidoc-ru

В качестве БД будем использовать PostgreSQL.Предусмотрим следующие обязательные для решения задачи роли:

  1. Сотрудник — для возможности пить чай;
  2. Удаленный сотрудник — для доступа к микросервису кракена (так как сотрудники в офисе не должны иметь возможности пить чай через точки распрастранения чая);
  3. Кракен — учетная запись микросервиса, что бы была возможность обращаться к API чайного сервиса;
  4. Авторизационная учетная запись — для предоставления доступа микросервисов к ролевой модели.

Mitm-атака для перехвата кода

Одной из реализаций OAuth является реализация с помощью внешнего браузера.

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

Именно в момент, когда система ищет приложение для обработки URL редиректа, возможен перехват редиректа зловредным приложением. Злоумышленник может создать приложение, которое перехватывает такие же редиректы, как у вас. Утекают все данные, которые находятся в строке редиректа.

Перехват редиректа в мобильном приложении
Перехват редиректа в мобильном приложении

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

При использовании обычного Authorization Code Flow чужое приложение (Malicious app) потенциально может получить код и обменять его на токен, аналогично тому, как это сделано в вашем приложении (Real app). Но с использованием code_verifier и code_challenge зловредный перехват становится бессмысленным.

Стоит отметить, что такая атака не сработает, если использовать universal links (ios) и applink (android). Чтобы открыть редирект-ссылку в приложении, необходимо положить на сервер json-файл с описанием подписи вашего приложения.

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

Авторизация

Теперь откроем страницу авторизации с использованием CCT.

Для работы с CCT и выполнения автоматических операций обмена кода на токен библиотека AppAuth предоставляет сущность AuthorizationService. Эта сущность создается при входе на экран. При выходе с экрана она должна очиститься. В примере это делается внутри ViewModel экрана авторизации.

Создаем в init:

private val authService: AuthorizationService = AuthorizationService(getApplication())

Очищаем в onCleared:

authService.dispose()

Для открытия страницы авторизации в CCT нужен интент. Для этого получаем AuthorizationRequest на основе заполненных раньше данных в AuthConfig:

private val serviceConfiguration = AuthorizationServiceConfiguration(
   Uri.parse(AuthConfig.AUTH_URI),
   Uri.parse(AuthConfig.TOKEN_URI),
   null, // registration endpoint
   Uri.parse(AuthConfig.END_SESSION_URI)
)

fun getAuthRequest(): AuthorizationRequest {
   val redirectUri = AuthConfig.CALLBACK_URL.toUri()
   return AuthorizationRequest.Builder(
       serviceConfiguration,
       AuthConfig.CLIENT_ID,
       AuthConfig.RESPONSE_TYPE,
       redirectUri
   )
       .setScope(AuthConfig.SCOPE)
       .build()
}

Создаем интент:

// тут можно настроить вид chromeCustomTabs
val customTabsIntent = CustomTabsIntent.Builder().build()

val openAuthPageIntent = authService.getAuthorizationRequestIntent(
   getAuthRequest(),
   customTabsIntent
)

После этого открываем активити по интенту. Нам необходимо обработать результат активити, чтобы получить код.

Поэтому используем ActivityResultContracts. Также можно использовать startActivityForResult.

private val getAuthResponse = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
   val dataIntent = it.data ?: return
   handleAuthResponseIntent(dataIntent)
}

getAuthResponse.launch(openAuthPageIntent)

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

Схема взаимодействия активити
Схема взаимодействия активити

Внутри openAuthPageIntent будет зашита вся информация, которую мы раньше указывали в AuthConfig, а также сгенерированный code_challenge.

Авторизация по протоколу oauth в проектах asp .net mvc

Процедура авторизации по протоколу OAuth в проектах ASP .NET MVC не сложнее, чем в проектах WebForms.

Первые два шага полностью идентичны.

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

C#:

Visual Basic .NET:

4. Добавьте в контроллер HomeController обработчик действия ExternalLoginResult.

C#:

Visual Basic .NET:

5. На странице разместите ссылки JavaScript, ведущие на действие ExternalLogin.

Примечание: Используйте ссылки JavaScript. Ни в коем случае не используйте прямые ссылки! Использование прямых ссылок может привести к утечке ресурсов, т.к. для каждой процедуры авторизации в памяти компьютера будет создаваться уникальная сессия. Процедура авторизации считается запущенной с момента получения ссылки на авторизацию.

6. Пользуйтесь!

Авторизация по протоколу oauth в проектах windows forms


В проектах Windows Forms осуществить авторизацию по протоколу OAuth можно при помощи стандартного элемента WebBrowser.

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

C#:

Visual Basic .NET:

2. Разместите на форме две кнопки и добавьте общий обработчик нажатия (btn_Click), который будет открывать окно авторизации. В свойстве Tag кнопок необходимо указать имя поставщика OAuth.

C#:

Visual Basic .NET:

3. Добавьте новую форму (далее frmLogin) и разместите на ней элемент WebBrowser.

4. Создайте для формы frmLogin конструктор, принимающий один строковой параметр. В этот параметр будет передаваться имя зарегистрированного в программе поставщика OAuth, по которому будет строиться адрес для авторизации.

C#:

Visual Basic .NET:

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

C#:

private void webBrowser1_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
{
  // ожидаем, когда появится адрес с результатами авторизации
  if (e.Url.Query.IndexOf("code=") != -1 || e.Url.Fragment.IndexOf("code=") != -1 || e.Url.Query.IndexOf("oauth_verifier=") != -1)
  {
    // проверяем адрес
    var result = OAuthWeb.VerifyAuthorization(e.Url.ToString());
    if (result.IsSuccessfully)
    {
      // показываем данные пользователя
      MessageBox.Show
      (
        String.Format
        (
          "User ID: {0}rnUsername: {1}rnDisplay Name: {2}rnE-Mail: {3}", 
          result.UserInfo.UserId,
          result.UserInfo.UserName,
          result.UserInfo.DisplayName ?? result.UserInfo.FullName,
          result.UserInfo.Email
        ), 
        "Successfully", 
        MessageBoxButtons.OK, 
        MessageBoxIcon.Information
      );
    }
    else
    {
      // ошибка
      MessageBox.Show(result.ErrorInfo.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
    }
    this.Close();
  }
}

Visual Basic .NET:

Private Sub WebBrowser1_DocumentCompleted(sender As System.Object, e As System.Windows.Forms.WebBrowserDocumentCompletedEventArgs) Handles WebBrowser1.DocumentCompleted
  ' ожидаем, когда появится адрес с результатами авторизации
  If Not e.Url.Query.IndexOf("code=") = -1 OrElse Not e.Url.Fragment.IndexOf("code=") = -1 OrElse Not e.Url.Query.IndexOf("oauth_verifier=") = -1 Then
    ' проверяем адрес
    Dim result = OAuthWeb.VerifyAuthorization(e.Url.ToString())
    If result.IsSuccessfully Then
      ' показываем данные пользователя
      MessageBox.Show _
      (
        String.Format _
        (
          "User ID: {0}{4}Username: {1}{4}Display Name: {2}{4}E-Mail: {3}",
          result.UserInfo.UserId,
          result.UserInfo.UserName,
          If(Not String.IsNullOrEmpty(result.UserInfo.DisplayName), result.UserInfo.DisplayName, result.UserInfo.FullName),
          result.UserInfo.Email,
          vbNewLine
        ),
        "Successfully",
        MessageBoxButtons.OK,
        MessageBoxIcon.Information
      )
    Else
      ' ошибка
      MessageBox.Show(result.ErrorInfo.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error)
    End If
    Me.Close()
  End If
End Sub

6. Пользуйтесь!

Авторизация работ (work authorization)

Требования аутентификации и авторизации API | learnapidoc-ruyoutube Проектная ПРАКТИКА 
Вконтакте Проектная ПРАКТИКА  
Яндекс Дзен Проектная ПРАКТИКА  
Телеграм Проектная ПРАКТИКА  
Требования аутентификации и авторизации API | learnapidoc-ru

Аутентификация по одноразовым паролям

Аутентификация по одноразовым паролям обычно применяется дополнительно к аутентификации по паролям для реализации
two-factor authentication
(2FA). В этой концепции пользователю необходимо предоставить данные двух типов для входа в систему: что-то, что он знает (например, пароль), и что-то, чем он владеет (например, устройство для генерации одноразовых паролей). Наличие двух факторов позволяет в значительной степени увеличить уровень безопасности, что м. б. востребовано для определенных видов веб-приложений.

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

Существуют разные источники для создания одноразовых паролей. Наиболее популярные:

  1. Аппаратные или программные токены, которые могут генерировать одноразовые пароли на основании секретного ключа, введенного в них, и текущего времени. Секретные ключи пользователей, являющиеся фактором владения, также хранятся на сервере, что позволяет выполнить проверку введенных одноразовых паролей. Пример аппаратной реализаций токенов — RSA SecurID; программной — приложение Google Authenticator.
  2. Случайно генерируемые коды, передаваемые пользователю через SMS или другой канал связи. В этой ситуации фактор владения — телефон пользователя (точнее — SIM-карта, привязанная к определенному номеру).
  3. Распечатка или scratch card со списком заранее сформированных одноразовых паролей. Для каждого нового входа в систему требуется ввести новый одноразовый пароль с указанным номером.

Требования аутентификации и авторизации API | learnapidoc-ru
Аппаратный токен RSA SecurID генерирует новый код каждые 30 секунд.

В веб-приложениях такой механизм аутентификации часто реализуется посредством расширения forms authentication: после первичной аутентификации по паролю, создается сессия пользователя, однако в контексте этой сессии пользователь не имеет доступа к приложению до тех пор, пока он не выполнит дополнительную аутентификацию по одноразовому паролю.

Аутентификация по сертификатам

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

На стороне клиента сертификат вместе с закрытым ключом могут храниться в операционной системе, в браузере, в файле, на отдельном физическом устройстве (smart card, USB token). Обычно закрытый ключ дополнительно защищен паролем или PIN-кодом.

В веб-приложениях традиционно используют сертификаты стандарта X.509. Аутентификация с помощью X.509-сертификата происходит в момент соединения с сервером и является частью протокола SSL/TLS. Этот механизм также хорошо поддерживается браузерами, которые позволяют пользователю выбрать и применить сертификат, если веб-сайт допускает такой способ аутентификации.

Требования аутентификации и авторизации API | learnapidoc-ru
Использование сертификата для аутентификации.

Во время аутентификации сервер выполняет проверку сертификата на основании следующих правил:

  1. Сертификат должен быть подписан доверенным certification authority (проверка цепочки сертификатов).
  2. Сертификат должен быть действительным на текущую дату (проверка срока действия).
  3. Сертификат не должен быть отозван соответствующим CA (проверка списков исключения).

Требования аутентификации и авторизации API | learnapidoc-ru
Пример X.509 сертификата.

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

Использование сертификатов для аутентификации — куда более надежный способ, чем аутентификация посредством паролей. Это достигается созданием в процессе аутентификации цифровой подписи, наличие которой доказывает факт применения закрытого ключа в конкретной ситуации (non-repudiation).

Аутентификация по токенам

Такой способ аутентификации чаще всего применяется при построении распределенных систем
Single Sign-On
(SSO), где одно приложение (
service provider
или
relying party
) делегирует функцию аутентификации пользователей другому приложению (
identity provider
или
authentication service
). Типичный пример этого способа — вход в приложение через учетную запись в социальных сетях. Здесь социальные сети являются сервисами аутентификации, а приложение
доверяет
функцию аутентификации пользователей социальным сетям.

Реализация этого способа заключается в том, что identity provider (IP) предоставляет достоверные сведения о пользователе в виде токена, а service provider (SP) приложение использует этот токен для идентификации, аутентификации и авторизации пользователя.На общем уровне, весь процесс выглядит следующим образом:

  1. Клиент аутентифицируется в identity provider одним из способов, специфичным для него (пароль, ключ доступа, сертификат, Kerberos, итд.).
  2. Клиент просит identity provider предоставить ему токен для конкретного SP-приложения. Identity provider генерирует токен и отправляет его клиенту.
  3. Клиент аутентифицируется в SP-приложении при помощи этого токена.

Требования аутентификации и авторизации API | learnapidoc-ru
Пример аутентификации «активного» клиента при помощи токена, переданного посредством Bearer схемы.

Процесс, описанный выше, отражает механизм аутентификации активного клиента, т. е. такого, который может выполнять запрограммированную последовательность действий (например, iOS/Android приложения). Браузер же — пассивный клиент в том смысле, что он только может отображать страницы, запрошенные пользователем.

Требования аутентификации и авторизации API | learnapidoc-ru
Пример аутентификации «пассивного» клиента посредством перенаправления запросов.

Существует несколько стандартов, в точности определяющих протокол взаимодействия между клиентами (активными и пассивными) и IP/SP-приложениями и формат поддерживаемых токенов. Среди наиболее популярных стандартов — OAuth, OpenID Connect, SAML, и WS-Federation. Некоторая информация об этих протоколах — ниже в статье.

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

При аутентификации с помощью токена SP-приложение должно выполнить следующие проверки:

  1. Токен был выдан доверенным identity provider приложением (проверка поля issuer).
  2. Токен предназначается текущему SP-приложению (проверка поля audience).
  3. Срок действия токена еще не истек (проверка поля expiration date).
  4. Токен подлинный и не был изменен (проверка подписи).

В случае успешной проверки SP-приложение выполняет авторизацию запроса на основании данных о пользователе, содержащихся в токене.

Делегирование полномочий

Давайте попробуем создать отдельный модуль. Назовём его совершенно бесхитростно — security.php, и оформим как отдельный скрипт. Будем подключать его ко всем закрытым страницам нашего проекта в самом начале. Внутри этого файла будем анализировать некие условия, а по итогам его работы выставлять специальный флаг в 0 или 1. Пусть этот флаг будет храниться в переменных сессии (массив $_SESSION в PHP).

Что нам это даёт? Мы можем запихать в этот скрипт сколь угодно хитрую логику, вплоть до анализа последних действий пользователя и добавления его в бан-лист по IP, либо блокировки его аккаунта на тот или иной срок. Но сперва реализуем очень базовую функциональность: будем сверять значение хэша, пришедшего из куки, с тем, что должно было бы получиться, если хэш не был искажён. Сервер знает IP, знает юзер-агент, знает пароль текущего пользователя… Кажется, всё готово!

Использовать библиотеки

Библиотеки должны поддерживать протоколы OAuth и OpenId и позволять общаться с любыми сервисами по этим протоколам. Примеры: AppAuth IOSAppAuth AndroidAuth0 Android

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

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

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

  • Учтите, что реализация библиотеки может быть не совсем удобной для встраивания в ваше приложение. Используемые подходы общения с библиотекой могут отличаться от принятых в команде, и нужно будет писать обертки-бриджи. Пример: AppAuth в Android использует AsyncTask под капотом, но в приложении вы, скорее всего, используете корутины. Но обычно такие вещи можно интегрировать.

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

Как быть?

Очевидно, с этим надо что-то делать. Да, можно сразу бежать покупать сертификат и подключать SSL. Но можно сделать кое-что ещё до этого, и существенно снизить тем самым необходимость в нём. В конце концов, в том же ВКонтакте SSL стал принудительным всего полгода назад, а до этого как-то ведь жили.

Первое, что приходит на ум — хранить вместо пароля хэш от него. Это не особо поможет, если кто-то перехватит трафик, и не поможет при краже кук. Но вот в ситуации «кто-то открыл настройки и прошёлся по кукам, пытаясь запомнить значения» — сильно усложнит жизнь злоумышленнику.

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

Как пользоваться библиотекой?

Использовать библиотеку очень просто. Для удобства, рекомендуется импортировать пространства имен Nemiro.OAuth и Nemiro.OAuth.Clients в проект.

C#:

Visual Basic .NET:


Пространство Nemiro.OAuth содержит основные классы, которые необходимы для работы с протоколами OAuth первой и второй версии.

В Nemiro.OAuth.Clients содержатся готовые реализации клиентов для популярных ресурсов.

Классы-клиенты можно использовать как есть, создавая новые экземпляры и вызывая определенные методы. Но лучше всего работать через два вспомогательных класса: OAuthManager и OAuthWeb.


Статичный класс OAuthManager позволяет регистрировать в приложении клиентов для различных серверов OAuth.

Маркер доступа

Маркер доступа, известный также как «access token» или просто «токен», необходим для работы с API сайтов, предоставляющих доступ к ресурсам своих пользователей по протоколу OAuth.

Каждый поставщик имеет свой уникальный API. Единой схемы работы с API, к сожалению, нет. Однако есть общие признаки, которые можно использовать при работе с различными интерфейсами.


В библиотеке Nemiro.OAuth базовая информация о пользователе получается при помощи API. Вы можете взять за основу методику работы с API и применить её для воплощения своих потребностей.

Не рекомендуется реализовывать работу с API непосредственно в библиотеке. Библиотека классов Nemiro.OAuth предназначена исключительно для работы с протоколом OAuth. Если вы хотите реализовать работу с API, лучше сделайте новый, узкопрофильный, проект, который будет принимать от Nemiro.OAuth маркер доступа и использовать его для запросов к методам API.

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

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

C#:

Visual Basic .NET:

Примечание: Если вы заметили, в примере также указано свойство Parameters. Это свойство содержит список параметров, которые будут переданы в адрес авторизации. В данном случае, будет передан параметр display=popup, что сообщит серверу ВКонтатке, что следует показывать модальное окно авторизации, а не стандартное.


Каждый поставщик OAuth имеет собственный набор прав. Информацию о доступных разрешениях вы можете найти в документации API на сайтах поставщиков OAuth.

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

Маркер доступа находится в результатах проверки авторизации пользователя, в свойстве AccessTokenValue. Разумеется, только если результат проверки был успешным (IsSuccessfully).

C#:

Visual Basic .NET:

В примере ниже, показано, как можно использовать маркер доступа для получения списка друзей авторизованного пользователя ВКонтакте.

C#:

private void GetVKontakteFriends(string accessToken)
{
  // параметры запроса
  var parameters = new NameValueCollection
  { 
    { "access_token", accessToken },
    { "count", "10" }, // только первые десять друзей
    { "fields", "nickname" }
  };

  // выполняем запрос к API
  var result = Nemiro.OAuth.Helpers.ExecuteRequest
  (
    "GET",
    "https://api.vk.com/method/friends.get",
    parameters,
    null
  );

  // выводим список друзей в окно консоли
  foreach (Dictionary<string, object> itm in (Array)result["response"])
  {
    string friendName = "";
    if (itm.ContainsKey("first_name") && itm.ContainsKey("last_name"))
    {
      friendName = String.Format("{0} {1}", itm["first_name"], itm["last_name"]);
    }
    else
    {
      friendName = itm["nickname"].ToString();
    }
    Console.WriteLine(friendName);
  }
}

Visual Basic .NET:

Public Sub GetVKontakteFriends(accessToken As String)
  ' параметры запроса
  Dim parameters As New NameValueCollection() From _
  {
    {"access_token", accessToken},
    {"count", "10"},
    {"fields", "nickname"}
  }

  ' выполняем запрос к API
  Dim result As RequestResult = Helpers.ExecuteRequest _
  (
    "GET",
    "https://api.vk.com/method/friends.get",
    parameters,
    Nothing
  )

  ' выводим список друзей в окно консоли
  For Each itm As Dictionary(Of String, Object) In CType(result("response"), Array)
    Dim friendName As String = ""
    If itm.ContainsKey("first_name") AndAlso itm.ContainsKey("last_name") Then
      friendName = String.Format("{0} {1}", itm("first_name"), itm("last_name"))
    Else
      friendName = itm("nickname").ToString()
    End If
    Console.WriteLine(friendName)
  Next
End Sub

Подключение библиотеки к проекту


Наиболее простым способом подключения библиотеки Nemiro.OAuth к проекту является использование консоли диспетчера пакетов (Package Manager Console).

Чтобы открыть окно консоли диспетчера пакетов (не путать с обычной консолью), выберите меню Сервис (Service) -> Диспетчер пакетов библиотек (Library Package Manager) -> Консоль диспетчера пакетов ( Package Manager Console).

Рис. 1. Вызов окна консоли диспетчера пакетов в Visual Studio.
Рис. 1. Вызов окна консоли диспетчера пакетов в Visual Studio.

В появившемся окошке введите команду:

Рис. 2. Установка Nemiro.OAuth через консоль диспетчера покетов.
Рис. 2. Установка Nemiro.OAuth через консоль диспетчера покетов.


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

Последствия нехватки безопасности api

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

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

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

В целом, аутентификация и авторизация с помощью API служат следующим целям:

Редирект в chrome не срабатывает

Как уже упоминали выше, для редиректа обратно в приложение лучше использовать кастомную схему, чтобы редирект не был перехвачен браузерами.

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

В Chrome сделали обновление, которое запрещает без пользовательского намерения переходить по URL с кастомной схемой. Это блокирует попадание пользователя в зловредное приложение. 

Для обхода этого ограничения сделали веб-страничку, на которую браузер редиректит после успешной авторизации. Веб-страница автоматически пытается сделать редирект уже внутрь приложения. Если этого не происходит, то есть Chrome заблокировал переход, пользователь может нажать на кнопку enter и перейти явно. Этот подход сработал.

Стандартная реализация

Итак, сессия в PHP по умолчанию хранится в файле. Её id сохраняется в cookie (если куки отключены — задействуется механизм передачи через адресную строку, если это не отключено в конфигурации). Время жизни такой сессионной куки по умолчанию — до момента закрытия браузера (и обычно его никто не меняет).

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

Если кто-то не понимает, чем это плохо — представьте себе, что у нас пользователь пользуется очень старым, либо очень плохо реализованным браузером. Мы ведь не можем гарантировать, что браузер надёжно шифрует куки? Да и вирусы всякие могут быть у пользователя на компьютере, и в случае особо серьёзной малвари может не спасти и шифровка — зловред может попытаться считать значения прямо из памяти, когда они расшифрованы.

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

Ну и самое главное — если у нас SSL используется только при авторизации (а на остальных страницах его решили отключить ради выигрыша в скорости, либо чтобы лучше работало промежуточное кэширование)… То наш пароль всё время передаётся открытым текстом.

Стандарты oauth и openid connect

В отличие от SAML и WS-Federation, стандарт OAuth (Open Authorization) не описывает протокол аутентификации пользователя. Вместо этого он определяет механизм получения доступа одного приложения к другому от имени пользователя. Однако существуют схемы, позволяющие осуществить аутентификацию пользователя на базе этого стандарта (об этом — ниже).

Первая версия стандарта разрабатывалась в 2007 – 2022 гг., а текущая версия 2.0 опубликована в 2022 г. Версия 2.0 значительно расширяет и в то же время упрощает стандарт, но обратно несовместима с версией 1.0. Сейчас OAuth 2.0 очень популярен и используется повсеместно для предоставления делегированного доступа и третье-сторонней аутентификации пользователей.

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

> Попросить пользователя указать данные своей учетной записи? — плохой вариант.> Попросить пользователя создать ключ доступа? — возможно, но весьма сложно.

Как раз эту проблему и позволяет решить стандарт OAuth: он описывает, как приложение путешествий (client) может получить доступ к почте пользователя (resource server) с разрешения пользователя (resource owner). В общем виде весь процесс состоит из нескольких шагов:

  1. Пользователь (resource owner) дает разрешение приложению (client) на доступ к определенному ресурсу в виде гранта. Что такое грант, рассмотрим чуть ниже.
  2. Приложение обращается к серверу авторизации и получает токен доступа к ресурсу в обмен на свой грант. В нашем примере сервер авторизации — Google. При вызове приложение дополнительно аутентифицируется при помощи ключа доступа, выданным ему при предварительной регистрации.
  3. Приложение использует этот токен для получения требуемых данных от сервера ресурсов (в нашем случае — сервис Gmail).

Требования аутентификации и авторизации API | learnapidoc-ru
Взаимодействие компонентов в стандарте OAuth.

Стандарт описывает четыре вида грантов, которые определяют возможные сценарии применения:

Стандарты ws-trust и ws-federation

WS-Trust и WS-Federation входят в группу стандартов WS-*, описывающих SOAP/XML-веб сервисы. Эти стандарты разрабатываются группой компаний, куда входят Microsoft, IBM, VeriSign и другие. Наряду с SAML, эти стандарты достаточно сложные, используются преимущественно в корпоративных сценариях.

Стандарт WS-Trust описывает интерфейс сервиса авторизации, именуемого Secure Token Service (STS). Этот сервис работает по протоколу SOAP и поддерживает создание, обновление и аннулирование токенов. При этом стандарт допускает использование токенов различного формата, однако на практике в основном используются SAML-токены.

Стандарт WS-Federation касается механизмов взаимодействия сервисов между компаниями, в частности, протоколов обмена токенов. При этом WS-Federation расширяет функции и интерфейс сервиса STS, описанного в стандарте WS-Trust. Среди прочего, стандарт WS-Federation определяет:

Можно сказать, что WS-Federation позволяет решить те же задачи, что и SAML, однако их подходы и реализация в некоторой степени отличаются.

Заключение

В статье предложена универсальная модель для авторизации доступа множеству пользователей к множеству ресурсов с минимальными потерями по производительности, позволяющая делать эффективное кеширование и хранение промежуточных данных на микросервисах, для сборки результата авторизации just-in-time.

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

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

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

Добавить комментарий

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