58.3. Аутентификация SASL #

SASL — это инфраструктура аутентификации для протоколов, ориентированных на соединения. На данный момент Postgres Pro реализует три механизма SASL: SCRAM-SHA-256, SCRAM-SHA-256-PLUS и OAUTHBEARER, а в будущем могут появиться и другие. Далее описывается, как в принципе осуществляется аутентификация SASL, а в следующих подразделах более подробно рассматриваются отдельные механизмы.

Поток сообщений аутентификации SASL

  1. Чтобы начать обмен по схеме аутентификации SASL, сервер передаёт сообщение AuthenticationSASL. Оно содержит список механизмов аутентификации SASL, с которыми может работать сервер, в порядке предпочтений сервера.

  2. Клиент выбирает один из поддерживаемых механизмов из списка и передаёт серверу сообщение SASLInitialResponse. Это сообщение содержит имя выбранного механизма и может содержать «Начальный ответ клиента», если это использует выбранный механизм.

  3. За этим следует одно или нескольких сообщений вызова со стороны сервера и ответов со стороны клиента. Все вызовы сервер передаёт в сообщениях AuthenticationSASLContinue, а клиент отвечает на них сообщениями SASLResponse. Частные детали сообщений зависят от конкретного механизма.

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

В случае ошибки сервер может прервать процесс аутентификации на любом этапе и передать сообщение ErrorMessage.

58.3.1. Аутентификация SCRAM-SHA-256 #

SCRAM-SHA-256 и его вариация со связыванием каналов, SCRAM-SHA-256-PLUS, относятся к механизмам аутентификации по паролю. Они подробно описываются в RFC 7677 и RFC 5802.

Когда в Postgres Pro задействуется SCRAM-SHA-256, сервер игнорирует имя пользователя, которое клиент передаёт в client-first-message. Вместо этого используется имя, переданное ранее в стартовом сообщении. Согласно спецификации SCRAM, имя пользователя должно быть в UTF-8, но Postgres Pro поддерживает разные кодировки символов, и значит, имя пользователя Postgres Pro не всегда будет представимо в UTF-8.

В спецификации SCRAM говорится, что пароль также должен передаваться в UTF-8 и обрабатываться алгоритмом SASLprep. Однако Postgres Pro не требует, чтобы пароль представлялся в UTF-8. Когда устанавливается пароль пользователя, он обрабатывается алгоритмом SASLprep как пароль в UTF-8, вне зависимости от фактической кодировки. Однако если он представлен недопустимой для UTF-8 последовательностью байтов либо содержит комбинации байтов UTF-8, которые не принимает алгоритм SASLprep, это не будет считаться ошибкой — при аутентификации будет использоваться исходный пароль, без обработки SASLprep. Это позволяет нормализовать пароли, представленные в UTF-8, и при этом использовать пароли не в UTF-8, а также не требует, чтобы система знала, в какой кодировке задан пароль.

Связывание каналов поддерживается в Postgres Pro при сборке с использованием SSL. Для SCRAM со связыванием каналов в качестве имени механизма SASL выбрано SCRAM-SHA-256-PLUS. Postgres Pro использует тип связывания tls-server-end-point.

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

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

Пример

  1. Сервер передаёт сообщение AuthenticationSASL. Оно содержит список механизмов аутентификации SASL, с которыми может работать сервер. Этот список будет включать SCRAM-SHA-256-PLUS и SCRAM-SHA-256, если сервер собран с поддержкой SSL, а иначе — только последнее значение.

  2. Клиент в ответ передаёт сообщение SASLInitialResponse, информирующее о выбранном механизме, SCRAM-SHA-256 или SCRAM-SHA-256-PLUS. (Клиент волен выбрать любой механизм, но для большей безопасности следует выбирать вариацию со связыванием каналов, если он это поддерживает.) В поле «Начальный ответ клиента» это сообщение содержит данные SCRAM client-first-message. В client-first-message содержится тип связывания каналов, выбранный клиентом.

  3. Сервер передаёт сообщение AuthenticationSASLContinue, содержащее данные SCRAM server-first-message.

  4. Клиент передаёт сообщение SASLResponse, содержащее данные SCRAM client-final-message.

  5. Сервер передаёт сообщение AuthenticationSASLFinal, содержащее данные SCRAM server-final-message, и сразу за ним сообщение AuthenticationOk.

58.3.2. Аутентификация OAUTHBEARER #

OAUTHBEARER — механизм на основе токенов для федеративной аутентификации. За подробностями обратитесь к RFC 7628.

Тип обмена зависит от того, есть ли в кеше у клиента токен типа bearer для текущего пользователя. Если токена нет, то обмен будет происходить через два соединения: первое соединение (discovery connection) нужно для получения метаданных OAuth от сервера, второе соединение после того, как клиент получит токен, — для его отправки пользователю. (В libpq на данный момент не реализован метод кеширования как часть встроенного потока, поэтому используется обмен через два соединения.)

Этот механизм инициируется клиентом, как и SCRAM. Начальный ответ клиента состоит из стандартного заголовка "GS2", используемого SCRAM, после которого выводится список пар key=value. Единственный ключ, который на данный момент поддерживается сервером, — auth, содержащий токен типа bearer. OAUTHBEARER дополнительно определяет три необязательных компонента начального ответа клиента (authzid заголовка GS2 и ключи host и port), которые на данный момент сервер игнорирует.

OAUTHBEARER не поддерживает привязку каналов. Механизм "OAUTHBEARER-PLUS" отсутствует. При успешной аутентификации в механизме не используются данные сервера, поэтому при обмене не передаётся сообщение AuthenticationSASLFinal.

Пример

  1. При первом обмене сервер передаёт клиенту сообщение AuthenticationSASL, предлагая механизм OAUTHBEARER.

  2. Клиент в ответ передаёт сообщение SASLInitialResponse, информирующее о выбранном механизме OAUTHBEARER. Если у клиента ещё нет действительного токена типа bearer для текущего клиента, поле auth пустое, что указывает на необходимость установления первого соединения (discovery connection).

  3. Сервер передаёт сообщение AuthenticationSASLContinue, содержащее ошибку status, а также известный URI и области доступа, которые клиент должен использовать для потока OAuth.

  4. Чтобы со своей стороны завершить первый обмен, клиент передаёт сообщение SASLResponse, содержащее пустой набор (один байт 0x01).

  5. Сервер передаёт сообщение ErrorMessage, чтобы прервать первый обмен.

    В этот момент, чтобы получить токен типа bearer, клиент реализует один из многочисленных доступных потоков OAuth, используя метаданные, с которыми он был настроен, а также те метаданные, которые ему передал сервер. (В этом описании намеренно опущены детали; OAUTHBEARER не определяет и не предписывает использование определённых методов для получения токена.)

    После получения токена клиент переподключается к серверу для завершения обмена:

  6. Сервер снова передаёт сообщение AuthenticationSASL, предлагая механизм OAUTHBEARER.

  7. Клиент в ответ передаёт сообщение SASLInitialResponse, но в этот раз поле auth содержит токен типа bearer, который был получен в ходе аутентификации клиента.

  8. Сервер проверяет токен в соответствии с инструкциями, полученными от поставщика токена. Если клиенту разрешено подключение, сервер передаёт сообщение AuthenticationOk для завершения обмена SASL.

58.3. SASL Authentication #

SASL is a framework for authentication in connection-oriented protocols. At the moment, Postgres Pro implements three SASL authentication mechanisms: SCRAM-SHA-256, SCRAM-SHA-256-PLUS, and OAUTHBEARER. More might be added in the future. The below steps illustrate how SASL authentication is performed in general, while the next subsections give more details on particular mechanisms.

SASL Authentication Message Flow

  1. To begin a SASL authentication exchange, the server sends an AuthenticationSASL message. It includes a list of SASL authentication mechanisms that the server can accept, in the server's preferred order.

  2. The client selects one of the supported mechanisms from the list, and sends a SASLInitialResponse message to the server. The message includes the name of the selected mechanism, and an optional Initial Client Response, if the selected mechanism uses that.

  3. One or more server-challenge and client-response message will follow. Each server-challenge is sent in an AuthenticationSASLContinue message, followed by a response from client in a SASLResponse message. The particulars of the messages are mechanism specific.

  4. Finally, when the authentication exchange is completed successfully, the server sends an optional AuthenticationSASLFinal message, followed immediately by an AuthenticationOk message. The AuthenticationSASLFinal contains additional server-to-client data, whose content is particular to the selected authentication mechanism. If the authentication mechanism doesn't use additional data that's sent at completion, the AuthenticationSASLFinal message is not sent.

On error, the server can abort the authentication at any stage, and send an ErrorMessage.

58.3.1. SCRAM-SHA-256 Authentication #

SCRAM-SHA-256, and its variant with channel binding SCRAM-SHA-256-PLUS, are password-based authentication mechanisms. They are described in detail in RFC 7677 and RFC 5802.

When SCRAM-SHA-256 is used in Postgres Pro, the server will ignore the user name that the client sends in the client-first-message. The user name that was already sent in the startup message is used instead. Postgres Pro supports multiple character encodings, while SCRAM dictates UTF-8 to be used for the user name, so it might be impossible to represent the Postgres Pro user name in UTF-8.

The SCRAM specification dictates that the password is also in UTF-8, and is processed with the SASLprep algorithm. Postgres Pro, however, does not require UTF-8 to be used for the password. When a user's password is set, it is processed with SASLprep as if it was in UTF-8, regardless of the actual encoding used. However, if it is not a legal UTF-8 byte sequence, or it contains UTF-8 byte sequences that are prohibited by the SASLprep algorithm, the raw password will be used without SASLprep processing, instead of throwing an error. This allows the password to be normalized when it is in UTF-8, but still allows a non-UTF-8 password to be used, and doesn't require the system to know which encoding the password is in.

Channel binding is supported in Postgres Pro builds with SSL support. The SASL mechanism name for SCRAM with channel binding is SCRAM-SHA-256-PLUS. The channel binding type used by Postgres Pro is tls-server-end-point.

In SCRAM without channel binding, the server chooses a random number that is transmitted to the client to be mixed with the user-supplied password in the transmitted password hash. While this prevents the password hash from being successfully retransmitted in a later session, it does not prevent a fake server between the real server and client from passing through the server's random value and successfully authenticating.

SCRAM with channel binding prevents such man-in-the-middle attacks by mixing the signature of the server's certificate into the transmitted password hash. While a fake server can retransmit the real server's certificate, it doesn't have access to the private key matching that certificate, and therefore cannot prove it is the owner, causing SSL connection failure.

Example

  1. The server sends an AuthenticationSASL message. It includes a list of SASL authentication mechanisms that the server can accept. This will be SCRAM-SHA-256-PLUS and SCRAM-SHA-256 if the server is built with SSL support, or else just the latter.

  2. The client responds by sending a SASLInitialResponse message, which indicates the chosen mechanism, SCRAM-SHA-256 or SCRAM-SHA-256-PLUS. (A client is free to choose either mechanism, but for better security it should choose the channel-binding variant if it can support it.) In the Initial Client response field, the message contains the SCRAM client-first-message. The client-first-message also contains the channel binding type chosen by the client.

  3. Server sends an AuthenticationSASLContinue message, with a SCRAM server-first-message as the content.

  4. Client sends a SASLResponse message, with SCRAM client-final-message as the content.

  5. Server sends an AuthenticationSASLFinal message, with the SCRAM server-final-message, followed immediately by an AuthenticationOk message.

58.3.2. OAUTHBEARER Authentication #

OAUTHBEARER is a token-based mechanism for federated authentication. It is described in detail in RFC 7628.

A typical exchange differs depending on whether or not the client already has a bearer token cached for the current user. If it does not, the exchange will take place over two connections: the first "discovery" connection to obtain OAuth metadata from the server, and the second connection to send the token after the client has obtained it. (libpq does not currently implement a caching method as part of its builtin flow, so it uses the two-connection exchange.)

This mechanism is client-initiated, like SCRAM. The client initial response consists of the standard "GS2" header used by SCRAM, followed by a list of key=value pairs. The only key currently supported by the server is auth, which contains the bearer token. OAUTHBEARER additionally specifies three optional components of the client initial response (the authzid of the GS2 header, and the host and port keys) which are currently ignored by the server.

OAUTHBEARER does not support channel binding, and there is no "OAUTHBEARER-PLUS" mechanism. This mechanism does not make use of server data during a successful authentication, so the AuthenticationSASLFinal message is not used in the exchange.

Example

  1. During the first exchange, the server sends an AuthenticationSASL message with the OAUTHBEARER mechanism advertised.

  2. The client responds by sending a SASLInitialResponse message which indicates the OAUTHBEARER mechanism. Assuming the client does not already have a valid bearer token for the current user, the auth field is empty, indicating a discovery connection.

  3. Server sends an AuthenticationSASLContinue message containing an error status alongside a well-known URI and scopes that the client should use to conduct an OAuth flow.

  4. Client sends a SASLResponse message containing the empty set (a single 0x01 byte) to finish its half of the discovery exchange.

  5. Server sends an ErrorMessage to fail the first exchange.

    At this point, the client conducts one of many possible OAuth flows to obtain a bearer token, using any metadata that it has been configured with in addition to that provided by the server. (This description is left deliberately vague; OAUTHBEARER does not specify or mandate any particular method for obtaining a token.)

    Once it has a token, the client reconnects to the server for the final exchange:

  6. The server once again sends an AuthenticationSASL message with the OAUTHBEARER mechanism advertised.

  7. The client responds by sending a SASLInitialResponse message, but this time the auth field in the message contains the bearer token that was obtained during the client flow.

  8. The server validates the token according to the instructions of the token provider. If the client is authorized to connect, it sends an AuthenticationOk message to end the SASL exchange.

FAQ