2025年9月25日: PostgreSQL 18 发布!
支持的版本: 当前 (18) / 17 / 16 / 15 / 14 / 13
开发版本: devel
不支持的版本: 12 / 11 / 10

54.3. SASL 身份验证 #

SASL 是面向连接协议中用于身份验证的框架。目前,PostgreSQL 支持三种 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。

54.3.1. SCRAM-SHA-256 身份验证 #

SCRAM-SHA-256 及其带有通道绑定的变体 SCRAM-SHA-256-PLUS 是基于密码的身份验证机制。它们在 RFC 7677RFC 5802 中有详细描述。

当 SCRAM-SHA-256 在 PostgreSQL 中使用时,服务器将忽略客户端在 client-first-message 中发送的用户名。而是使用在启动消息中已发送的用户名。PostgreSQL 支持多种字符编码,而 SCRAM 要求用户名使用 UTF-8,因此可能无法用 UTF-8 表示 PostgreSQL 用户名。

SCRAM 规范要求密码也使用 UTF-8,并使用 SASLprep 算法进行处理。PostgreSQL 然而,不要求密码必须使用 UTF-8。当设置用户的密码时,无论实际使用的编码如何,它都会像 UTF-8 一样经过 SASLprep 处理。但是,如果它不是合法的 UTF-8 字节序列,或者它包含 SASLprep 算法禁止的 UTF-8 字节序列,则将使用原始密码而无需 SASLprep 处理,而不是抛出错误。这使得密码在 UTF-8 编码时可以被规范化,但仍然允许使用非 UTF-8 密码,并且不需要系统知道密码的编码。

通道绑定 在支持 SSL 的 PostgreSQL 构建中得到支持。带有通道绑定的 SCRAM 的 SASL 机制名称是 SCRAM-SHA-256-PLUS。PostgreSQL 使用的通道绑定类型是 tls-server-end-point

SCRAM不带通道绑定时,服务器会选择一个随机数,该随机数会传输给客户端,并与用户提供的密码一起混合在传输的密码哈希中。虽然这可以防止在后续会话中成功重放密码哈希,但它并不能阻止真实服务器和客户端之间的一个伪造服务器传递服务器的随机值并成功进行身份验证。

SCRAM带有通道绑定时,通过将服务器证书的签名混合到传输的密码哈希中,可以防止这种中间人攻击。虽然伪造服务器可以重放真实服务器的证书,但它无法访问与该证书匹配的私钥,因此无法证明其所有权,从而导致 SSL 连接失败。

示例:

  1. 服务器发送一个 AuthenticationSASL 消息。它包含服务器可以接受的 SASL 身份验证机制列表。如果服务器构建时支持 SSL,则此列表将包含 SCRAM-SHA-256-PLUSSCRAM-SHA-256,否则仅包含后者。

  2. 客户端通过发送 SASLInitialResponse 消息进行响应,该消息指示所选的机制 SCRAM-SHA-256SCRAM-SHA-256-PLUS。(客户端可以自由选择任一机制,但为了提高安全性,如果客户端支持,应选择带通道绑定的变体。)在初始客户端响应字段中,消息包含 SCRAM client-first-messageclient-first-message 还包含客户端选择的通道绑定类型。

  3. 服务器发送一个 AuthenticationSASLContinue 消息,其内容为 SCRAM server-first-message

  4. 客户端发送一个 SASLResponse 消息,其内容为 SCRAM client-final-message

  5. 服务器发送一个 AuthenticationSASLFinal 消息,其中包含 SCRAM server-final-message,然后立即发送一个 AuthenticationOk 消息。

54.3.2. OAUTHBEARER 身份验证 #

OAUTHBEARER 是一种基于令牌的联合身份验证机制。它在 RFC 7628 中有详细描述。

典型的交换过程根据客户端是否已经为当前用户缓存了持有者令牌而有所不同。如果客户端没有缓存令牌,则交换将通过两个连接进行:第一个“发现”连接用于从服务器获取 OAuth 元数据,第二个连接用于在客户端获取令牌后发送令牌。(libpq 目前在其内置流程中不实现缓存方法,因此它使用双连接交换。)

此机制是客户端发起的,类似于 SCRAM。客户端初始响应包括 SCRAM 使用的标准“GS2”标头,后跟一个 key=value 对列表。服务器目前支持的唯一键是 auth,它包含持有者令牌。OAUTHBEARER 额外指定了客户端初始响应的三个可选组件(GS2 标头的 authzid,以及 hostport 键),这些组件目前被服务器忽略。

OAUTHBEARER 不支持通道绑定,也没有“OAUTHBEARER-PLUS”机制。此机制在成功身份验证期间不使用服务器数据,因此在交换过程中不使用 AuthenticationSASLFinal 消息。

示例:

  1. 在第一次交换期间,服务器会发送一个 AuthenticationSASL 消息,其中广告宣传了 OAUTHBEARER 机制。

  2. 客户端通过发送 SASLInitialResponse 消息进行响应,该消息指示 OAUTHBEARER 机制。假设客户端尚未为当前用户持有有效的持有者令牌,则 auth 字段为空,表示这是发现连接。

  3. 服务器发送一个 AuthenticationSASLContinue 消息,其中包含一个错误 status 以及客户端应使用的用于进行 OAuth 流程的通用 URI 和范围。

  4. 客户端发送一个 SASLResponse 消息,其中包含空集(一个 0x01 字节)以完成其在发现交换中的部分。

  5. 服务器发送一个 ErrorMessage 以中止第一次交换。

    此时,客户端将执行多种可能的 OAuth 流程之一来获取持有者令牌,除了服务器提供的元数据外,还可以使用任何配置给它的元数据。(此描述故意保持模糊;OAUTHBEARER 不指定或强制要求任何特定的获取令牌的方法。)

    一旦获得令牌,客户端将重新连接到服务器进行最终交换

  6. 服务器再次发送一个 AuthenticationSASL 消息,其中广告宣传了 OAUTHBEARER 机制。

  7. 客户端通过发送 SASLInitialResponse 消息进行响应,但这次消息中的 auth 字段包含在客户端流程中获取的持有者令牌。

  8. 服务器根据令牌提供商的指示验证令牌。如果客户端被授权连接,则发送 AuthenticationOk 消息以结束 SASL 交换。

提交更正

如果您在文档中看到任何不正确之处,与您对特定功能的体验不符,或需要进一步澄清,请使用 此表格 报告文档问题。