4. MQTT 3.1.1与5.0认证能力对比

在 MQTT 3.1.1 中,身份认证几乎完全依赖 CONNECT 报文里的用户名和密码字段。协议本身只规定这两个字段“可以携带”,至于密码如何编码、服务端如何校验、失败后如何反馈,全部交给实现自行决定。这带来一个直接后果:不同 Broker 的认证行为差异很大,客户端很难用统一方式判断“我到底是因为密码错、账号被锁,还是服务端暂时不可用而被拒绝”。到了 MQTT 5.0,协议层新增了 AUTH 报文、增强认证(Enhanced Authentication)、原因码和认证方法字段,把认证从“一次性提交凭据”扩展为可多轮交互、可携带挑战响应、可承载多因素流程的机制。理解这两代协议的差异,是设计可演进认证体系的前提。

3.1.1 的认证模型:单次提交、结果二元

MQTT 3.1.1 的认证过程可以概括为:客户端在 CONNECT 中携带 Client ID、用户名、密码(可选),服务端校验后返回 CONNACK,其中只有“接受”或“拒绝”两种结果。CONNACK 的返回码虽然区分了“不支持的协议版本”“标识符被拒绝”“服务端不可用”“用户名或密码错误”“未授权”等,但很多 Broker 在实际部署中并不严格区分,客户端往往只能看到“连接被拒绝”。

这种模型下,密码通常以明文或简单哈希形式传输,安全性依赖 TLS 保护。若要在 3.1.1 上实现多因素认证,常见做法是把第二因素“塞进”密码字段,例如“密码:OTP”拼接,或者要求客户端先通过 HTTP 接口换取一次性 Token,再把 Token 当作密码。这些做法能工作,但属于应用层约定,协议不感知,Broker 也无法在认证过程中向客户端发起挑战。

5.0 的增强认证:AUTH 报文与挑战响应

MQTT 5.0 引入了 AUTH 报文(类型 15),它与 CONNECT、CONNACK 配合,形成可多轮往返的认证握手。关键字段包括:

  • Authentication Method:字符串,标识认证方法,例如 "SCRAM-SHA-1"、"GS2-KRB5" 或自定义方法名。客户端和服务端必须使用相同方法,否则连接失败。
  • Authentication Data:二进制数据,承载挑战、响应、盐值、迭代次数等具体内容,格式由所选方法定义。
  • Reason Code:在 AUTH 和 CONNACK 中指示认证进展,例如 0x18 继续认证、0x19 重新认证、0x86 认证方法不支持、0x87 未授权等。

典型挑战响应流程如下:客户端发送 CONNECT,携带 Authentication Method 和初始 Authentication Data;服务端若需要继续,返回 CONNACK,原因码为 0x18(继续认证),并附带服务端挑战数据;客户端再发送 AUTH 报文,携带响应数据;服务端可继续返回 AUTH 挑战,直到最终返回 CONNACK,原因码为 0x00(成功)或错误码。整个过程在 TLS 之上运行,Authentication Data 本身可以再加密或签名。

MQTT 3.1.1 与 5.0 认证能力对比 MQTT 3.1.1:单次凭据提交 客户端 CONNECT Broker 校验用户名/密码 CONNECT CONNACK 用户名 + 密码(明文/哈希) 结果:接受 / 拒绝(二元) 无挑战、无多轮、无方法协商 多因素扩展方式 密码字段拼接 OTP / Token 依赖应用层约定,协议不感知 安全边界:TLS 保护传输 凭据一次性提交,无法动态挑战 MQTT 5.0:增强认证(AUTH) 客户端 CONNECT + Method Broker 发起挑战 CONNECT CONNACK 0x18 AUTH 响应 AUTH 挑战 AUTH 响应 CONNACK 0x00 关键字段 Authentication Method(方法协商) Authentication Data(挑战/响应/盐值) Reason Code(0x18 继续 / 0x00 成功) 支持重新认证(0x19) 演进

从“单次提交”到“多轮协商”的能力跃迁

把两代协议放在一起看,差异不只是“多了一个 AUTH 报文”,而是认证控制权的转移。3.1.1 中,认证节奏完全由客户端决定:客户端一次性把凭据发完,服务端只能接受或拒绝。5.0 中,服务端可以在 CONNACK 中返回 0x18,主动要求客户端继续提供数据,客户端再用 AUTH 报文响应。这意味着服务端可以按需发起挑战,例如先要用户名,再要 OTP,再要设备指纹,而不必要求客户端在第一次 CONNECT 时就凑齐所有因素。

另一个关键差异是“认证方法”成为协议字段。3.1.1 没有方法协商,客户端和 Broker 之间靠约定;5.0 中,Authentication Method 不匹配会直接导致连接失败,原因码为 0x8C(Bad Authentication Method)。这让 SCRAM、Kerberos、OAuth 2.0 Token 交换等机制可以在协议层被识别,而不是伪装成“密码”。

多因素扩展的实际落地方式

在 5.0 中实现多因素认证,通常有两种路径。第一种是选择已有的增强认证方法,例如 SCRAM-SHA-256,它本身提供挑战响应和密码证明,但不直接覆盖第二因素;可以在 Authentication Data 中扩展自定义字段,把 TOTP 验证码作为附加数据传入。第二种是定义自定义 Authentication Method,例如 "MFA-TOTP",由客户端和服务端约定数据格式:服务端先返回随机挑战 nonce,客户端用共享密钥计算 HMAC 并附带 TOTP,服务端验证后返回成功。

下面是一个自定义方法的交互示例,展示服务端如何分两步收集密码证明和动态验证码:

客户端 → CONNECT
  Authentication Method: "MFA-TOTP"
  Authentication Data: username="alice"

服务端 → CONNACK (Reason Code 0x18)
  Authentication Data: challenge="a3f8c1", salt="9b2e", iterations=4096

客户端 → AUTH
  Authentication Data: proof=HMAC-SHA256(key, challenge), totp="482913"

服务端 → CONNACK (Reason Code 0x00)
  认证成功,连接建立

这里的关键判断依据是:服务端在第二步必须校验 proof 和 totp 是否同时有效;若 proof 正确但 totp 过期,应返回 0x87(未授权)而不是 0x18,避免客户端无限重试。客户端则应根据 Reason Code 决定是继续发送 AUTH,还是提示用户重新输入验证码。

迁移与兼容性考量

从 3.1.1 迁移到 5.0 时,认证能力的升级不是自动的。Broker 需要同时支持两代协议,并对 3.1.1 客户端保持用户名密码路径;对 5.0 客户端,则可以在 CONNACK 中声明支持哪些 Authentication Method。客户端若检测到 Broker 不支持增强认证,应回退到用户名密码模式,而不是直接失败。对于安全要求较高的场景,建议在 5.0 连接上强制使用增强认证,并通过 ACL 限制仅允许已完成增强认证的客户端访问敏感主题。

实践提示:在测试增强认证时,先用 mosquitto_sub 的 5.0 模式观察 CONNACK 原因码。若返回 0x18,说明服务端期望继续认证;若返回 0x8C,说明 Authentication Method 不被支持;若返回 0x87,则凭据校验失败。根据原因码定位问题,比在 3.1.1 下猜测“密码错还是权限错”要高效得多。

总结来看,MQTT 3.1.1 的认证是“一次性凭据提交”,适合简单账号密码场景;MQTT 5.0 通过 AUTH 报文、Authentication Method 和 Reason Code,把认证变成可协商、可多轮、可扩展的握手过程,为挑战响应和多因素认证提供了协议级支撑。实际治理中,应根据客户端能力和安全等级,决定哪些连接走增强认证、哪些保留兼容路径,并用 ACL 把认证结果与授权范围绑定起来。