50.1. Безопасное проектирование модуля проверки #

Предупреждение

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

50.1.1. Задачи модуля проверки #

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

Проверка токена

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

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

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

Если токен нельзя проверить, модуль должен немедленно завершить работу с ошибкой. Дальнейшая аутентификация и авторизация лишены смысла, если токен типа bearer не был выдан доверенной стороной.

Авторизация клиента

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

Цель этого шага — предотвратить получение токена клиентом OAuth под ложным предлогом. Если модуль проверки требует, чтобы области доступа всех токенов обеспечивали доступ к базе данных, в процессе аутентификации поставщик должен явно запросить у пользователя разрешение на такой доступ. Это даёт пользователю возможность отклонить запрос, если клиент не должен использовать его учётные данные для подключения к базе данных.

Хотя авторизация клиентов без явного указания областей доступа возможна при наличии дополнительной информации о развёрнутой архитектуре, такой подход исключает из процесса пользователя. Это лишает его возможности обнаруживать ошибки развёртывания и позволяет злоумышленникам незаметно использовать подобные ошибки. Доступ к базе данных должен быть строго ограничен только доверенными клиентами [17], если у пользователей не запрашиваются дополнительные области доступа.

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

Аутентификация конечного пользователя

В заключение модуль проверки должен определить идентификатор пользователя для токена, либо запросив эту информацию у поставщика, либо извлекая её из самого токена, и вернуть этот идентификатор серверу. Сервер затем примет окончательное решение об авторизации, используя конфигурацию HBA. Идентификатор пользователя можно запросить в рамках сеанса, вызвав функцию system_user. Также он будет регистрироваться в журнале сервера, если включён параметр log_connections.

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

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

50.1.2. Общие рекомендации по написанию кода #

При реализации проверки токенов разработчикам необходимо учитывать следующее:

Конфиденциальность токенов

Модули не должны записывать токены или их части в журнал сервера. Это правило действует, даже если модуль считает токен недействительным. Злоумышленник, который может перенаправить клиента на поддельного поставщика, не должен иметь возможность получить этот (а в ином случае и действительный) токен с диска.

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

Протоколирование событий

Модули проверки могут использовать те же средства протоколирования, что и стандартные расширения. Однако правила выдачи записей журнала клиенту на этапе аутентификации подключения имеют свои особенности. Вообще говоря, чтобы избежать утечки информации неаутентифицированным клиентам, модули должны протоколировать проблемы проверки с уровнем COMMERROR и завершать работу штатным образом, вместо того чтобы записывать события с уровнем ERROR/FATAL и инициировать раскрутку стека.

Возможность прерывания

Чтобы сервер мог корректно обрабатывать тайм-ауты аутентификации и сигналы остановки от pg_ctl, необходима возможность прерывать работу модулей по сигналу. Например, блокирующие вызовы сокетов, как правило, следует заменять кодом, который обрабатывает как события сокетов, так и прерывания без условий гонки (например, функции WaitLatchOrSocket(), WaitEventSetWait() и другие), а длительные циклы должны периодически вызывать CHECK_FOR_INTERRUPTS(). Несоблюдение этих рекомендаций может привести к тому, что некоторые сеансы обслуживающих процессов перестанут отвечать.

Тестирование

Объём тестирования системы OAuth выходит далеко за рамки данной документации, однако, как минимум, негативное тестирование следует считать обязательным. Спроектировать модуль, который пропускает авторизованных пользователей, довольно просто. Но основная задача системы заключается в том, чтобы не допустить неавторизованных пользователей.

Документация

При реализации модулей проверки следует документировать содержимое и формат аутентифицированного идентификатора, который передаётся серверу для каждого конечного пользователя (например, является ли этот идентификатор адресом электронной почты, идентификатором организации или UUID). Эта информация может потребоваться администраторам баз данных для создания файлов сопоставления pg_ident. Также должно быть указано, безопасно ли использовать модуль в режиме delegate_ident_mapping=1, и какая дополнительная конфигурация для этого необходима.

50.1.3. Авторизация пользователей (делегирование сопоставления пользователей) #

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

Чтобы полностью обойти сопоставление имён пользователей и передать модулю проверки дополнительную задачу по авторизации подключений пользователей, HBA можно настроить с помощью параметра delegate_ident_mapping. При этом модуль может использовать области действия токенов или равнозначный метод, чтобы определить, разрешено ли пользователю подключаться под запрашиваемой ролью. Идентификатор пользователя будет по-прежнему записываться сервером, но не будет влиять на решение о продолжении подключения.

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

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



[17] «Доверенным» считается такой клиент OAuth, который контролируется той же организацией, что и сервер PostgreSQL. Следует отметить, что клиентский поток авторизации устройства, поддерживаемый libpq, не всегда соответствует этому требованию, поскольку он предназначен для использования публичными или недоверенными клиентами.