SASL 是面向连接协议的认证框架。目前,PostgreSQL 实现了两种 SASL 认证机制,即 SCRAM-SHA-256 和 SCRAM-SHA-256-PLUS。未来可能增加更多认证机制。以下步骤说明了 SASL 认证的一般执行方式,而下一小节将详细介绍 SCRAM-SHA-256 和 SCRAM-SHA-256-PLUS。
SASL 认证消息流
要开始 SASL 认证交换,服务器会发送 AuthenticationSASL 消息。该消息包括服务器可以接受的 SASL 认证机制列表(按服务器的首选顺序)。
客户端从列表中选择一种受支持的机制,并向服务器发送 SASLInitialResponse 消息。该消息包括所选机制的名称以及可选的初始客户端响应(如果所选机制使用该响应)。
接下来会出现一个或多个 Server-Challenge 和 Client-Response 消息。每次 Server-Challenge 都在 AuthenticationSASLContinue 消息中发送,随后是客户端在 SASLResponse 消息中的响应。消息的详细信息是特定于机制的。
最后,当身份验证交换成功完成后,服务器会发送 AuthenticationSASLFinal 消息,紧随其后是一个 AuthenticationOk 消息。AuthenticationSASLFinal 包含额外的服务器对客户端数据,其内容特定于所选的身份验证机制。如果身份验证机制不使用在完成时发送的附加数据,AuthenticationSASLFinal 消息将不会发送。
出现错误时,服务器可以在任何阶段中止身份验证,并发送一个 ErrorMessage。
当前已实施的 SASL 机制为 SCRAM-SHA-256
和其具有 channel binding 的变体 SCRAM-SHA-256-PLUS
。它们在 RFC 7677 和 RFC 5802 中进行了详细描述。
当在 PostgreSQL 中使用 SCRAM-SHA-256 时,服务器将忽略客户端在 client-first-message
中发送的用户名。而使用启动消息中已发送的用户名。虽然 PostgreSQL 支持多种字符编码,而 SCRAM 规定将 UTF-8 用于用户名,因此可能无法在 UTF-8 中表示 PostgreSQL 用户名。
SCRAM 规范规定密码也必须为 UTF-8,并且需要使用 SASLprep 算法进行处理。然而,PostgreSQL 不需要对密码使用 UTF-8。当设置用户的密码时,它将使用 SASLprep 处理密码,无论使用的是哪种实际编码。但是,如果这不是合法的 UTF-8 字节序列,或者它包含 SASLprep 算法禁止的 UTF-8 字节序列,则将使用原始密码,而不会进行 SASLprep 处理,也不会引发错误。这允许在 UTF-8 中对密码进行规范化,但仍然允许使用非 UTF-8 密码,并且不需要系统知道密码采用的是哪种编码。
Channel binding 在支持 SSL 的 PostgreSQL 版本中受到支持。具有 channel binding 的 SCRAM 的 SASL 机制名称为 SCRAM-SHA-256-PLUS
。PostgreSQL 使用的 channel binding 类型是 tls-server-end-point
。
在SCRAM如果未使用通道绑定,服务器会选择一个随机数传输给客户端,并将其与用户提供的密码混合成传输的密码哈希。虽然这可以防止密码哈希在后续会话中被成功再次传输,但却无法防止真正的服务器与客户端之间的虚假服务器传递服务器的随机值并成功通过身份验证。
SCRAM使用通道绑定可通过将服务器证书的签名混合到传输的密码哈希中来防止此类中间人攻击。虚假服务器虽然可以再次传输真正的服务器证书,但它无权访问与该证书匹配的私钥,因此无法证明自己是所有者,从而导致 SSL 连接失败。
示例
服务器发送 AuthenticationSASL 消息。其中包括服务器可以接受的 SASL 身份验证机制列表。如果服务器构建了 SSL 支持,则这将是 SCRAM-SHA-256-PLUS
和 SCRAM-SHA-256
,否则只是后者。
客户端通过发送 SASLInitialResponse 消息进行响应,其中指出所选机制 SCRAM-SHA-256
或 SCRAM-SHA-256-PLUS
。(客户端可以选择任意一种机制,但为了提高安全性,如果客户端能够支持,则应选择通道绑定变体。)在初始客户端响应字段中,消息包含 SCRAM client-first-message
。此外,client-first-message
也包含客户端选择的通道绑定类型。
服务器发送 AuthenticationSASLContinue 消息,内容为 SCRAM server-first-message
。
客户端发送 SASLResponse 消息,内容为 SCRAM client-final-message
。
服务器发送 AuthenticationSASLFinal 消息,内容为 SCRAM server-final-message
,随后紧接着一个 AuthenticationOk 消息。