SASL是面向连接协议中的认证框架。目前,PostgreSQL实现了三种 SASL 机制:SCRAM-SHA-256、SCRAM-SHA-256-PLUS 和 OAUTHBEARER。未来可能继续增加。下面的步骤说明 SASL 认证的一般流程,后续小节将介绍具体机制细节。
SASL认证消息流
要开始一个SASL认证交换,服务器发送一个AuthenticationSASL消息。它包括服务器可以接受的SASL认证机制列表,按照服务器的首选顺序排列。
客户端从列表中选择一个支持的机制,并向服务器发送一个SASLInitialResponse消息。 该消息包括所选机制的名称,以及如果所选机制使用的话,还包括一个可选的初始客户端响应。
一个或多个服务器挑战和客户端响应消息将随后而来。每个服务器挑战都是在一个 AuthenticationSASLContinue 消息中发送的,随后是客户端在一个 SASLResponse 消息中的响应。这些消息的具体内容是特定于机制的。
最后,当认证交换成功完成时,服务器会发送可选的 AuthenticationSASLFinal 消息,随后立即发送 AuthenticationOk。AuthenticationSASLFinal 包含机制特定的附加数据;若该机制不需要完成时附加数据,则不会发送 AuthenticationSASLFinal。
在错误情况下,服务器可以在任何阶段中止认证,并发送一个ErrorMessage。
SCRAM-SHA-256 及其带通道绑定的变体 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编码。 当用户设置密码时,无论实际使用的编码是什么,都会像使用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连接失败。
示例
服务器发送一个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消息,其中包含一个SCRAMserver-first-message作为内容。
客户端发送一个SASLResponse消息,其中包含SCRAM client-final-message作为内容。
服务器发送一个AuthenticationSASLFinal消息,带有SCRAM server-final-message,紧接着是一个AuthenticationOk消息。
OAUTHBEARER 是一种基于令牌的联合认证机制,详见 RFC 7628。
典型交互取决于客户端是否已经为当前用户缓存 bearer token。若没有,通常会经历两次连接:第一次“发现连接”用于从服务器获取 OAuth 元数据,第二次连接在客户端拿到 token 后再发送 token。(libpq 当前内置流程尚未实现缓存,因此使用两连接流程。)
该机制与 SCRAM 一样由客户端发起。客户端初始响应由 SCRAM 使用的标准 GS2 头部加上一组 key=value 对组成。当前服务器仅支持 auth 键(即 bearer token)。OAUTHBEARER 还定义了三个可选组成(GS2 头部中的 authzid,以及 host/port),当前服务器会忽略它们。
OAUTHBEARER 不支持通道绑定,因此不存在 “OAUTHBEARER-PLUS”。该机制在成功认证时不使用额外服务器数据,所以不会使用 AuthenticationSASLFinal 消息。
示例
第一次交互时,服务器发送 AuthenticationSASL,并在机制列表中声明 OAUTHBEARER。
客户端发送 SASLInitialResponse 并选择 OAUTHBEARER。若尚无可用 token,则 auth 为空,表示这是发现连接。
服务器发送 AuthenticationSASLContinue,包含错误 status、well-known URI 以及客户端应使用的 scopes。
客户端发送 SASLResponse,内容为空集合(单个 0x01 字节),结束发现阶段的客户端半程。
服务器发送 ErrorMessage,使第一次交互失败。随后客户端按配置和服务器提供的元数据执行 OAuth 流程以获取 token。(具体获取方式由客户端实现决定,OAUTHBEARER 不强制规定。)
客户端拿到 token 后重新连接;服务器再次发送 AuthenticationSASL,并声明 OAUTHBEARER。
客户端再次发送 SASLInitialResponse,这次在 auth 中携带 bearer token。
服务器按令牌提供方规则校验 token。若客户端被授权连接,服务器发送 AuthenticationOk,认证完成。
如果您发现文档中有不正确的内容、与您使用特定功能的经验不符或需要进一步说明,请使用此表单来报告文档问题。