54.3. SASL 认证 #
SASL 是面向连接协议中的认证框架。目前,PostgreSQL 实现了三种 SASL 机制:SCRAM-SHA-256、SCRAM-SHA-256-PLUS 和 OAUTHBEARER。未来可能继续增加。下面的步骤说明 SASL 认证的一般流程,后续小节将介绍具体机制细节。
SASL 认证消息流
要开始一个 SASL 认证交换,服务器发送一个 AuthenticationSASL 消息。它包括服务器可以接受的 SASL 认证机制列表,按照服务器的首选顺序排列。
客户端从列表中选择一种受支持的机制,并向服务器发送 SASLInitialResponse 消息。消息包含所选机制的名称;如果该机制使用初始客户端响应,消息还可以包含这一可选响应。
随后会进行一轮或多轮服务器挑战和客户端响应。每次服务器挑战都通过 AuthenticationSASLContinue 消息发送,随后客户端通过 SASLResponse 消息响应。消息的具体内容取决于所用机制。
最后,认证交换成功完成时,服务器会发送可选的 AuthenticationSASLFinal 消息,紧接着发送 AuthenticationOk 消息。AuthenticationSASLFinal 包含从服务器发给客户端的附加数据,其具体内容取决于所选认证机制。如果该认证机制不使用在完成时发送的附加数据,则不会发送 AuthenticationSASLFinal 消息。
在错误情况下,服务器可以在任何阶段中止认证,并发送一个 ErrorMessage。
54.3.1. SCRAM-SHA-256 认证 #
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 中,服务器会选择一个随机数并发送给客户端,将它与用户提供的密码混合,形成所传输的密码 hash。虽然这样可以防止在后续会话中成功重放该密码 hash,但无法阻止位于真实服务器与客户端之间的伪造服务器转发服务器的随机值并成功通过认证。
带通道绑定的 SCRAM 会将服务器证书的签名混入所传输的密码 hash,从而防止此类中间人攻击。虽然伪造服务器可以转发真实服务器的证书,但它无法取得与证书匹配的私钥,因此无法证明自己是证书所有者,最终导致 SSL 连接失败。
示例
服务器发送一个 AuthenticationSASL 消息。它包括服务器可以接受的 SASL 认证机制列表。如果服务器构建时支持 SSL,这将是
SCRAM-SHA-256-PLUS和SCRAM-SHA-256,否则只是后者。客户端通过发送 SASLInitialResponse 消息做出响应,该消息指示选择的机制,
SCRAM-SHA-256或SCRAM-SHA-256-PLUS。(客户端可以自由选择任一机制,但为了更好的安全性,如果支持的话应选择通道绑定变体。)在初始客户端响应字段中,消息包含 SCRAMclient-first-message。client-first-message还包含客户端选择的通道绑定类型。服务器发送一个 AuthenticationSASLContinue 消息,其中包含一个 SCRAM
server-first-message作为内容。客户端发送一个 SASLResponse 消息,其中包含 SCRAM
client-final-message作为内容。服务器发送一个 AuthenticationSASLFinal 消息,带有 SCRAM
server-final-message,紧接着是一个 AuthenticationOk 消息。
54.3.2. OAUTHBEARER 认证 #
OAUTHBEARER 是一种基于令牌的联合认证机制。其详细说明见 RFC 7628。
典型交互取决于客户端是否已为当前用户缓存 Bearer 令牌。如果没有,交互将通过两次连接完成:第一次“发现”连接从服务器获取 OAuth 元数据,第二次连接在客户端取得令牌后发送该令牌。(libpq 的内置流程目前没有实现缓存方法,因此使用两次连接的交互方式。)
该机制与 SCRAM 一样由客户端发起。客户端初始响应由 SCRAM 使用的标准“GS2”头部以及随后的一组 key=value 对组成。服务器目前唯一支持的键是 auth,其中包含 Bearer 令牌。OAUTHBEARER 还规定了客户端初始响应的三个可选部分:GS2 头部的 authzid,以及 host 和 port 键;服务器目前会忽略它们。
OAUTHBEARER 不支持通道绑定,也不存在“OAUTHBEARER-PLUS”机制。该机制在成功认证期间不使用服务器数据,因此交互中不使用 AuthenticationSASLFinal 消息。
示例
第一次交互期间,服务器发送 AuthenticationSASL 消息,并声明支持
OAUTHBEARER机制。客户端发送 SASLInitialResponse 消息作为响应,在其中指定
OAUTHBEARER机制。假定客户端尚未持有当前用户的有效 Bearer 令牌,则auth字段为空,表示这是发现连接。服务器发送 AuthenticationSASLContinue 消息,其中包含错误
status、well-known URI,以及客户端执行 OAuth 流程时应使用的授权范围。客户端发送包含空集合(单个
0x01字节)的 SASLResponse 消息,以结束发现交互中由客户端完成的部分。服务器发送 ErrorMessage,使第一次交互失败。
此时,客户端会执行多种可用 OAuth 流程中的一种,以获取 Bearer 令牌;所使用的元数据包括客户端已配置的元数据,以及服务器提供的元数据。(此处的描述有意保持宽泛;
OAUTHBEARER不指定也不强制要求采用任何特定的令牌获取方法。)取得令牌后,客户端会重新连接服务器,进行最后一次交互:
服务器再次发送 AuthenticationSASL 消息,并声明支持
OAUTHBEARER机制。客户端发送 SASLInitialResponse 消息作为响应,但这次消息中的
auth字段包含客户端流程取得的 Bearer 令牌。服务器按照令牌提供者的说明验证令牌。如果客户端获准连接,服务器便发送 AuthenticationOk 消息,结束 SASL 交互。