摘要

  • 480 表示命令在连接当前的身份状态下遭到拒绝,而不是服务器把它暂存到认证完成以后。
  • AUTHINFO 成功后,客户端需要重新查看当时可用的能力,并主动再次发出原来的资源请求。
  • 认证只回答服务器接受了哪个身份;本地授权策略仍可对重发的命令返回 502。

两条相同命令之间发生了什么

观察一段很短的会话。客户端发送 GROUP local.research,服务器返回 480 Permission denied。客户端发现认证方式,完成 AUTHINFO,收到 281 Authentication accepted。此时,服务器仍没有选择那个新闻组。

只有当客户端第二次发送 GROUP local.research,服务器才会在新的身份状态下评估请求。第一条命令已经有了终结性答复。它既没有进入等待队列,也没有与随后出现的凭据捆绑起来。

读者软件可以把这个过程包装成一次顺滑的“登录后继续”,但线上的权力关系更细。第一次请求表达当时的意图;480 说明当时的会话状态不足;认证建立一个主体;第二次请求才说明主体在新状态下仍愿意执行那项操作。

早期实现把“重试”写进了规则

RFC 2980 记录了当时已经广泛存在的 NNTP 扩展。原始的 AUTHINFO USER 与 AUTHINFO PASS 流程从 480 开始,可能用 381 索取密码,并以 281 表示用户名和密码被接受。随后,客户端应当重新发送曾收到 480 的原命令,由服务器按正常流程处理这一次新请求。

这项安排把身份与行为拆成两份证据。用户名和密码用于回答“当前连接声称是谁”,但不能替客户端回答“先前那件事现在还要不要做”。认证期间,用户可能取消操作,客户端可能发现能力已经变化,资源也可能不再值得访问。显式重发让当前意图留在协议记录里。

早期机制同时暴露了严重缺陷:RFC 2980 明确说所有认证信息都以明文传送。命令边界设计得正确,并不等于秘密得到了保护。没有额外的加密层,密码一旦越过可观察链路,就无法靠稍后补上的保护恢复机密性。

身份通过,不代表资源放行

RFC 4643 后来正式定义 AUTHINFO,出于兼容保留 USER/PASS,淘汰 SIMPLE 与 GENERIC,并为 NNTP 引入 SASL。它首先明确的不是某种算法,而是管理边界:AUTHINFO 负责认证用户,授权则由站点策略决定。

因此,480 只说明使用这条命令或访问这项资源之前需要认证和/或授权。认证可能消除障碍,却不承诺消除。服务器完全可以接受身份,然后因为该主体没有目标新闻组的权限而返回 502。RFC 4643 专门把认证后的永久拒绝与认证前可能改变的 480 区分开来。

281 的证据范围也必须被限制。它证明这一次会话的认证交换被服务器接受,不证明用户拥有所有组的读取权、发布权或对等传输权;更不证明某篇文章由该账户创作,也不会变成内容签名或跨服务器通行证。

能力清单属于会话中的一个时刻

准备使用 AUTHINFO 的客户端应先发送 CAPABILITIES。返回的列表不是服务器产品说明书,而是当前连接在当前状态下可用的功能。TLS 建立前后、认证前后或运行模式变化前后,列表都可能不同。

认证成功后,服务器必须停止公布 AUTHINFO,并以 502 拒绝同一会话中的再次认证。其他能力可以随身份而增减;原本隐藏的组或命令可能在此时出现。因此,客户端在决定重试什么以前,需要重新读取能力,而不能把认证前的快照当作永久合同。

有一项刻意保留:SASL 机制列表在认证后仍须以相同内容公布,让客户端有机会发现主动降级的迹象。它是谈判证据,不是允许第二次认证。AUTHINFO 的入口已经关闭,SASL 列表留下来只为核对先前看到的选择集。

如果服务器在 281 后自动执行旧命令,就可能用一套客户端尚未观察的新能力环境处理旧意图。要求重发,让客户端先看到状态变化,再决定行动。

认证过程本身也不能无限续接

客户端可以在收到 480 后启动 AUTHINFO,也可以在能力允许时主动认证。但第一步之后能否继续,必须由服务器的 38x 响应明确邀请。其他任何响应都意味着认证交换结束,客户端不得继续提交后续材料。

服务器也绝不能对 AUTHINFO 本身返回 480。否则,用来满足认证要求的命令会再次触发同一个要求,形成没有出口的循环。历史上的 381 还具有特殊含义:它要求下一条独立的 AUTHINFO PASS,而不是一般意义上给当前命令追加数据。

认证一旦成功,同一会话不得再次切换身份。想换主体,客户端应建立新连接。这个单向性避免新身份接手旧身份遗留的操作,也让权限变化拥有可审计的连接边界。

加密是另一道门

NNTP 用 483 表示缺少适当的隐私保护。RFC 4642 定义 STARTTLS;TLS 完成后,先前应用状态需要重建,客户端也必须重新读取能力。只有进入受保护信道后,明文 USER/PASS 才可能被服务器公布;SASL 选择也可能发生变化。

这里至少有四个彼此独立的问题:链路有没有加密、会话身份有没有建立、该身份有没有特定资源权限、客户端是否在新状态下重新表达了操作。TLS 不替用户选择新闻组,AUTHINFO 不分配所有权限,授权存在也不意味着旧命令仍是当前意图。

链路保护也不是文章的端到端身份证明。TLS 只覆盖一次 NNTP 连接;AUTHINFO 认证的是会话客户端,而不是文章作者,也不能保证后续转发链路的保护。

拒绝应当留下证据,而不是留下待执行任务

RFC 3977 给出了访问受限设施的例子:客户端先请求一个组,收到 480,完成认证扩展,然后再次发送同一 GROUP 命令。这个顺序直接说明,先前的命令已经失败,并未在服务器内部暂停。

运维记录应保留每一个边界:原请求、480、认证前能力、TLS 状态、所选认证机制、认证结果、认证后能力、显式重试以及最终资源判断。若把它们压缩成一个“登录成功”,就无法分辨密码错误、权限缺失、客户端没有重试,还是服务器错误地重放了旧请求。

第二条命令即使字节相同,语境也不同。它发生在不同时间、不同主体与不同能力集之下,可以成功,也可以得到 502、遇到别的临时故障,或因为用户改变决定而根本不出现。

注册表协调名称,不授予权限

IANA NNTP 参数注册表 分别登记 AUTHINFO、SASL 与 STARTTLS,并指向不同的规范。共同名称让客户端和服务器能够准确描述每一种状态转换。

登记不证明某台服务器当前支持它们,不证明 USER/PASS 在任何信道都安全,也不证明某账户有权访问某个组。真正的证据仍是这条连接此刻的能力响应、认证结果,以及客户端重发命令后收到的资源答复。

NNTP 留下的长期原则是:当系统因身份不足而拒绝操作时,更强的身份可以修复环境,但不应自动取得重放权。服务器可以知道是谁在说话,却仍要等对方重新说明现在想做什么。

来源