跳到主要内容

6.2 客户端命令 - 未认证状态

在未认证状态下, AUTHENTICATE 或 LOGIN 命令建立认证并进入 authenticated 状态. AUTHENTICATE 命令为多种认证技术, 隐私保护和完整性检查提供通用机制, 而 LOGIN 命令使用传统的用户名和明文密码对, 且没有建立隐私保护或完整性检查的手段.

STARTTLS 命令是建立 session 隐私保护和完整性检查的另一种形式, 但它本身不会建立认证, 也不会进入 authenticated 状态.

服务器实现 MAY 允许在未建立认证的情况下访问某些 mailbox. 这可以通过 [ANONYMOUS] 中描述的 ANONYMOUS [SASL] authenticator 完成. 较旧的约定是使用 userid "anonymous" 的 LOGIN 命令; 在这种情况下需要密码, 尽管服务器可以选择接受任何密码. 对匿名用户施加的限制取决于实现.

一旦完成认证 (包括匿名认证), 就无法重新进入未认证状态.

除通用命令 (CAPABILITY, NOOP 和 LOGOUT) 外, 以下命令在未认证状态下有效: STARTTLS, AUTHENTICATE 和 LOGIN. 有关这些命令的重要信息见 Security Considerations (第 11 节).

6.2.1 STARTTLS 命令

参数:

响应: 此命令没有特定响应

结果:

  • OK - starttls 已完成, 开始 TLS 协商
  • NO - 由于服务器配置错误, 无法发起 TLS 协商
  • BAD - 在成功 TLS 协商后收到 STARTTLS, 或参数无效

注意, STARTTLS 命令仅在明文端口上可用. 当在 Implicit TLS 端口上收到 STARTTLS 命令时, 服务器 MUST 始终以带标签 BAD 响应应答.

TLS [TLS-1.3] 协商在服务器带标签 OK 响应末尾的 CRLF 之后立即开始. 客户端一旦发出 STARTTLS 命令, 在看到服务器响应且 TLS 协商完成之前, MUST NOT 发出更多命令. 过去某些服务器实现错误实现了 STARTTLS 处理, 已知包含 STARTTLS 明文命令注入漏洞 [CERT-555316]. 为避免此漏洞, 如果在启动 STARTTLS 命令的 CRLF 之后, 同一 TCP buffer 中还收到任何数据, 服务器实现 MUST 执行以下操作之一:

  1. 将 TCP buffer 中的额外数据解释为 TLS handshake 的开始. (如果该数据为明文, 这将导致 TLS handshake 失败.)

  2. 丢弃 TCP buffer 中的额外数据.

注意, 第一种选项对将 STARTTLS 命令开头与 TLS handshake 数据流水线发送的客户端更友好.

TLS 协商成功后, 即使在 TLS 协商期间提供了客户端凭据, 服务器仍保持未认证状态. 这并不排除 EXTERNAL ([SASL] 中定义) 等认证机制使用由 TLS 协商确定的客户端身份.

一旦 TLS 已启动, 客户端 MUST 丢弃关于服务器 capability 的缓存信息, 并 SHOULD 重新发出 CAPABILITY 命令. 这是为了防御在 STARTTLS 之前篡改 capability 列表的主动攻击. 在 STARTTLS 命令成功后, 服务器 MAY 通告不同的 capability, 尤其 SHOULD NOT 再通告 STARTTLS capability.

示例:

C: a001 CAPABILITY
S: * CAPABILITY IMAP4rev2 STARTTLS LOGINDISABLED
S: a001 OK CAPABILITY completed
C: a002 STARTTLS
S: a002 OK Begin TLS negotiation now
<TLS negotiation, further commands are under TLS layer>
C: a003 CAPABILITY
S: * CAPABILITY IMAP4rev2 AUTH=PLAIN
S: a003 OK CAPABILITY completed
C: a004 AUTHENTICATE PLAIN dGVzdAB0ZXN0AHRlc3Q=
S: a004 OK Success (tls protection)

6.2.2 AUTHENTICATE 命令

参数:

  • SASL 认证机制名称
  • OPTIONAL initial response

响应: 可以请求 continuation data

结果:

  • OK - authenticate 已完成, 现在处于 authenticated 状态
  • NO - authenticate 失败: 不支持的认证机制, 凭据被拒绝
  • BAD - 命令未知或参数无效, 认证交换被取消

AUTHENTICATE 命令向服务器指示一个 [SASL] 认证机制. 如果服务器支持所请求的认证机制, 它会执行认证协议交换以认证并识别客户端. 它 MAY 还为后续协议交互协商一个 OPTIONAL security layer. 如果不支持所请求的认证机制, 服务器 SHOULD 通过发送带标签 NO 响应来拒绝 AUTHENTICATE 命令.

AUTHENTICATE 命令支持 [SASL] 第 4 节定义的可选 "initial response" 特性. 客户端不需要使用它. 如果某个 SASL 机制支持 "initial response", 但客户端未指定, 服务器按 [SASL] 第 3 节中的规定处理.

本协议的 [SASL] profile 指定的服务名称为 "imap".

认证协议交换由一系列特定于认证机制的服务器 challenge 和客户端 response 组成. 服务器 challenge 由带有 "+" token 的命令 continuation request 响应后接 base64 编码字符串 (见 [RFC4648] 第 4 节) 组成. 客户端 response 由单行 base64 编码字符串组成. 如果客户端希望取消认证交换, 它发出一行仅包含 "*" 的内容. 如果服务器收到这样的 response, 或收到无效 base64 字符串 (例如 base64 字母表之外的字符或非结尾的 "="), 它 MUST 通过发送带标签 BAD 响应来拒绝 AUTHENTICATE 命令.

与任何其他客户端 response 一样, initial response MUST 编码为 base64. 它也 MUST 在 quoted string 或 literal 之外传输. 要发送零长度 initial response, 客户端 MUST 发送单个填充字符 ("="). 这表示 response 存在, 但它是零长度字符串.

解码 initial response 中的 base64 数据时, 解码错误 MUST 按任何普通 SASL 客户端 response 的方式处理, 即使用带标签 BAD 响应. 特别是, 服务器应检查 base64 字母表未明确允许的任何字符, 以及任何在字符串末尾以外位置包含填充字符 ('=') 的 base64 字符序列 (例如不允许 "=AAA" 和 "AAA=BBB").

如果客户端对不支持 initial response 的 SASL 机制使用 initial response, 服务器 MUST 使用带标签 BAD 响应拒绝该命令.

如果通过 [SASL] 认证交换协商了 security layer, 它会在客户端结束认证交换的 CRLF 以及服务器带标签 OK 响应的 CRLF 之后立即生效.

虽然客户端和服务器实现 MUST 实现 AUTHENTICATE 命令本身, 但不要求实现 [PLAIN] 中描述的 PLAIN 机制以外的任何认证机制. 此外, 认证机制不要求支持任何 security layer.

注意: 服务器实现 MUST 实现一种配置, 除非已经协商 STARTTLS 命令, 已在 Implicit TLS 端口上协商 TLS, 或已提供其他保护 session 免受密码窥探的机制, 否则该配置不允许任何明文密码机制. 服务器站点 SHOULD NOT 使用任何允许明文密码机制但没有此类防密码窥探保护机制的配置. 客户端和服务器实现 SHOULD 实现不使用明文密码的其他 [SASL] 机制, 例如 [RFC4752] 中描述的 GSSAPI 机制, SCRAM-SHA-256/SCRAM-SHA-256-PLUS [SCRAM-SHA-256] 机制, 和/或用于 mutual TLS 认证的 EXTERNAL [SASL] 机制.

服务器和客户端可以支持多个认证机制. 服务器 SHOULD 在对 CAPABILITY 命令的响应中列出其支持的认证机制, 以便客户端知道要使用哪些认证机制.

服务器 MAY 在成功 AUTHENTICATE 命令的带标签 OK 响应中包含 CAPABILITY response code, 以便自动发送 capability. 如果客户端识别这些自动 capability, 就不必发送单独的 CAPABILITY 命令. 只有在 AUTHENTICATE 命令未协商 security layer 时才应这样做, 因为作为 AUTHENTICATE 命令一部分的带标签 OK 响应不受加密/完整性检查保护. 在这种情况下, [SASL] 要求客户端重新发出 CAPABILITY 命令. 成功 AUTHENTICATE 命令之后, 服务器 MAY 通告不同 capability.

如果 AUTHENTICATE 命令以 NO 响应失败, 客户端 MAY 通过发出另一个 AUTHENTICATE 命令尝试另一种认证机制. 它 MAY 也尝试使用 LOGIN 命令进行认证 (更多细节见第 6.2.3 节). 换言之, 客户端 MAY 按偏好递减顺序请求认证类型, 将 LOGIN 命令作为最后手段.

认证交换期间从客户端传递给服务器的 authorization identity 会被服务器解释为客户端请求其权限的用户名.

示例: (完整 GSSAPI 示例见中文版本)

带 initial response 的示例:

S: * OK [CAPABILITY IMAP4rev2 STARTTLS AUTH=GSSAPI LOGINDISABLED] Server ready
C: A01 STARTTLS
S: A01 OK STARTTLS completed
<TLS negotiation, further commands are under TLS layer>
C: A02 CAPABILITY
S: * CAPABILITY IMAP4rev2 AUTH=GSSAPI AUTH=PLAIN
S: A02 OK CAPABILITY completed
C: A03 AUTHENTICATE PLAIN dGVzdAB0ZXN0AHRlc3Q=
S: A03 OK Success (tls protection)

6.2.3 LOGIN 命令

参数:

  • 用户名
  • 密码

响应: 此命令没有特定响应

结果:

  • OK - login 已完成, 现在处于 authenticated 状态
  • NO - login 失败: 用户名或密码被拒绝
  • BAD - 命令未知或参数无效

LOGIN 命令向服务器标识客户端, 并携带用于认证该用户的明文密码. 除作为最后手段 (一次或多次尝试使用 AUTHENTICATE 命令认证失败之后) 外, SHOULD NOT 使用 LOGIN 命令, 并建议客户端实现提供禁用自动使用 LOGIN 命令的手段.

服务器 MAY 在成功 LOGIN 命令的带标签 OK 响应中包含 CAPABILITY response code, 以便自动发送 capability. 如果客户端识别这些自动 capability, 就不必发送单独的 CAPABILITY 命令.

示例:

C: a001 LOGIN SMITH SESAME
S: a001 OK LOGIN completed

注意: 在不安全网络 (如 Internet) 上使用 LOGIN 命令存在安全风险, 因为任何监视网络流量的人都可以获得明文密码. 因此, 客户端 MUST NOT 在不安全网络上使用 LOGIN.

除非客户端正在通过 Implicit TLS 端口 [RFC8314] 访问 IMAP 服务, 已协商 STARTTLS 命令, 或已提供其他保护 session 免受密码窥探的机制, 否则服务器实现 MUST 实现一种配置, 在该配置中通告 LOGINDISABLED capability 且不允许 LOGIN 命令. 服务器站点 SHOULD NOT 使用任何允许 LOGIN 命令但没有此类防密码窥探保护机制的配置. 如果通告了 LOGINDISABLED capability, 客户端实现 MUST NOT 发送 LOGIN 命令.