跳到主要内容

打开导航

返回博客

MQTT 认证详解:密码、JWT、SCRAM 与 AUTH 报文

理解 MQTT 认证机制,逐项配置 Mqttable 客户端的认证方式,并用真实实验区分连接认证、TLS 保护与 Topic 权限。

作者:
Mqttable Editorial
发布:
手绘 MQTT 认证封面,展示客户端、密钥、凭据卡片与 Broker 之间的双向消息交换。

MQTT 客户端能访问 Broker,不一定能通过连接认证。即使连接成功,订阅某个 Topic 也可能被拒绝。这是两个不同阶段的问题,不能都靠换密码解决。

本文先说明认证机制,再结合 Mqttable 的新建与编辑 Client 表单逐项配置、验证。本文由 Mqttable 发布,使用它作为操作示例;理解和实现这些机制不要求使用 Mqttable。产品截图来自隔离的源码 Web Runtime,凭据均为一次性本地实验材料,不代表桌面安装包、托管服务或生产部署通过验收。

认证、AUTH、TLS 和 ACL 分别解决什么问题

认证确定 Broker 接受的客户端身份。授权决定这个身份可以发布、订阅哪些 Topic,常见实现是 Topic ACL。TLS保护传输并验证服务端身份;双向 TLS 还可以验证客户端证书。

MQTT 5 的 AUTH 报文比“认证”这个词的范围小。它承载 SCRAM 等增强认证交换,不是所有认证方式统一使用的报文。普通密码认证不需要 AUTH;把 JWT 放进 CONNECT 的 Password 字段也不需要 AUTH。MQTT 5 定义交换框架,不要求每个 Broker 都实现同一种密码库、JWT 签发方或 SCRAM 算法。协议依据见 MQTT 5 第 3.15、4.12 节。

示意图区分 TLS 传输保护、MQTT 客户端认证和 Topic 授权。

这是机制示意图,不是抓包结果。某一层通过,不能证明下一层也通过。

Client ID 也不是密码。它标识 MQTT 会话,不能与其他在线客户端冲突。Broker 可以把它纳入认证或授权策略,但仅知道别人的 Client ID,本身不是安全的身份认证。

Mqttable 在 Broker 中配置传输协议、服务器证书信任和 mTLS,在 Client 中配置用户名、密码和认证方式。CA Certificate 验证的是服务端信任链,不能代替客户端的 MQTT 密码。如果还没到 MQTT 认证阶段就失败,先看 TLS 证书指南。

六种 Authentication Method 怎么选

当前 MQTT 5 Client 表单提供六个选项。切换到 MQTT 3.1 或 3.1.1 时,认证方式下拉会隐藏,但用户名、密码字段仍然存在。这是 Mqttable 的界面边界,不意味着 MQTT 3.1.1 不支持认证,也不意味着它不能通过密码字段传递 Token。

左右滚动表格,查看其余列。

方式需要的材料MQTT 连接中传递什么Broker 前提
无(None)不使用 MQTT 密码;匿名示例还要清空用户名不发送密码,非空用户名仍可能发送明确允许该客户端不带密码连接
密码(Password)Broker 签发的用户名、密码,或密码 SecretRefCONNECT 的 Username、Password对应的密码认证器和账号
JWT完整签名 Token;按策略填写用户名此模式把 Token 放进 CONNECT 的 Password从 Password 读取 JWT,认可签名密钥和 claims
SCRAM-SHA-1用户名、密码SCRAM 属性和挑战响应数据,不使用普通 CONNECT 密码字段明确支持这一旧版机制
SCRAM-SHA-256用户名、密码使用精确方法名的 MQTT 5 增强认证支持 SCRAM-SHA-256
SCRAM-SHA-512用户名、密码使用精确方法名的 MQTT 5 增强认证支持 SCRAM-SHA-512

按运营方配置的机制选择,不是哪个名字看着更强就选哪个。SHA-256 和 SHA-512 是两个不同的 SCRAM 方法,客户端不能自行替换。本地 EMQX 6.0.0 实验支持这两种变体,不支持 SHA-1。这是实验 Broker 的边界,不代表 Mqttable 没有 SHA-1 选项。EMQX 的 SCRAM 文档列出了支持的哈希算法。

这六个客户端选项也不是所有 MQTT 部署的身份系统清单。Broker 可以验证证书、调用 HTTP 认证服务或查询数据库。客户端通常只需要知道这些服务要求的报文机制和凭据。

None 与 Password:从匿名连接到账号认证

None:明确匿名实验的边界

只有 Listener 允许时才用 None。本实验的匿名 Mosquitto Listener 仅绑定 127.0.0.1,不是公网匿名服务。

  1. 保存指向这个 Listener 的 Broker,打开新建 Client。
  2. MQTT 版本选 5.0,认证方式选无(None)。
  3. 清空用户名。密码控件会禁用;选择 None 只抑制密码,不一定抑制用户名。
  4. 使用唯一 Client ID,点击测试连接。
Mqttable 的 MQTT 5 Client 表单选择无认证,密码控件处于禁用状态。

None 描述客户端如何处理密码。这个身份能不能连接,仍由 Broker 决定。

Password:凭据和传输保护分别配置

选择密码(Password),填写运营方提供的 MQTT 用户名,单次测试可以选择明文密码(Raw password)。云控制台账号不一定就是 MQTT 账号,要以部署里的 MQTT 认证设置为准。

Mqttable 密码认证使用原始密码输入,密码保持遮蔽。

Client 的测试连接使用当前表单,包括尚未保存的凭据;截图不会显示密码原文。

CONNECT 中的用户名和密码不会给传输加密。凭据经过不可信网络时,使用正确验证服务器身份的 mqtts 或 wss 端点。本文明文 Listener 仅用于 loopback 实验,不是生产安全配置建议。

需要重复使用时,先在**凭据(Credentials)**中创建 Password Secret,再到 Client 选择 Saved SecretRef。选择器显示受保护引用的元信息,不显示原文。引用缺失或已删除,不是可以忽略的“空密码”。保存边界见凭据与 TLS。

Mqttable 选择已保存的密码 SecretRef,不渲染密码原文。

SecretRef 改变 Mqttable 从哪里取得凭据,不改变 Broker 的密码策略,也不会给 CONNECT 加密。

JWT:Token 放在哪里,Broker 验证什么

JWT 是 Token 格式,不是新的 MQTT 报文。Mqttable 的这个模式把 Token 放进 Password。Broker 也要从同一字段读取 JWT。有些 Broker 支持从 Username 读取,但选择 Mqttable 的 JWT 模式不会自动切换到那种配置。EMQX 文档分别说明了两种位置。

认证服务已经给出完整 Token 时,直接粘贴到 Password,或保存成 Password SecretRef,不要重新签名。生产环境应按签发方的用户或设备认证流程取得 Token,不能为了让普通客户端自行生成凭据,把签发方的 HMAC 主密钥或 RSA 私钥分发给它们。

HS256:共享签名密钥

受控本地实验中,选择 JWT,打开 JWT 工具(JWT Tools),算法选 HMAC(HS256),输入一次性共享密钥,再按 Broker 要求设置 claims。本实验把 username 与 MQTT Username 绑定,并检查 iss=mqttable-auth-lab、aud=mqttable-auth-demo。

Mqttable JWT Tools 使用 HS256,签名密钥保持遮蔽,claims 可编辑。

HS256 使用共享密钥签名。默认的 username、iat、exp 行可以修改,不是所有 Broker 通用的认证策略。

点击 生成并填入(Generate & Fill),把 Token 填入密码草稿,再看 预览(Preview)。签名密钥不是 Token。Broker 先使用对应密钥验证签名,再独立检查 claims。

RS256:私钥签名,公钥验证

RS256 的本地签名端需要 PEM 私钥,Broker 需要匹配的公钥。不要把私钥上传到 Broker 的 JWT 公钥验证字段。私钥不能进入截图、共享日志或文章示例文件。

Mqttable JWT Tools 选择 RSA RS256,显示录入私钥之前的 PEM 输入区域。

这张配置图拍摄于录入私钥之前。认证结果另外实测,空密钥字段不是已经签名或认证成功的证明。

本地实验生成 RSA 密钥对,只把公钥配置到 EMQX,再用私钥签发客户端 Token。HS256、RS256 都进行连接验证,但不把它写成外部身份提供方或 JWKS 接入验收。

claims、Preview 与 JWT Debugger

左右滚动表格,查看其余列。

claim要检查的问题
exp按验证方的时钟,Token 是否已经过期?
nbf是否还没到允许使用的时间?
iat什么时候签发,Broker 是否检查签发时间策略?
iss签发方是否与配置完全一致?
audToken 是否签发给当前接受它的服务?
sub代表哪个主体,如何映射到权限?

不是每个 Broker 都要求所有 claim。字段类型、时钟容差也要按运营方策略配置。本实验的 username 是额外检查,不代替标准的 sub。RFC 7519定义了这些注册 claim。

JWT Tools 的**预览(Preview)**解码当前草稿,方便阅读 header 和 claims。在 JWT 调试器中打开提供单独的本地检查流程。在本次验证的源码中,调试器验签支持 HS256、HS384、HS512,不支持 RS256。已实际验证 HS256 签名匹配和返回原草稿;RS256 的接受结果来自 Broker,不来自这个调试器。Base64url 解码不等于验证签名;签名正确也可能因为签发方、受众或过期策略不匹配而失败。这里展示的签名 JWS payload 可读,并没有加密。

从 Debugger 返回经过检查或编辑的 Token,不会保存 Client,也不会连接 Broker。这个跳转不会自动取出隐藏的已保存凭据,也不会把签名密钥传给 Debugger。要针对返回后的密码草稿重新测试。Mqttable 的连接路径不会获取、自动续期或刷新 Token。

Mqttable Client 测试连接拒绝一个已经过期的本地 JWT。

这是本实验观察到的拒绝。单独一个通用认证错误码,不能证明失败原因一定是过期,而不是签名或其他 claim 不匹配。

Token 过期后是否断开已经在线的客户端,由 Broker 策略决定。它不同于 CONNECT 时拒绝过期 Token,也不是客户端选了 JWT 就自动具备的行为。

SCRAM 与 MQTT 5 AUTH 的实际交换

SCRAM 使用用户名和密码构造挑战响应。Mqttable 的 SCRAM 模式不把密码放进普通 CONNECT Password 字段。这避免把原始密码作为基础 MQTT 凭据传输,但不会给业务消息加密,也不取消对受保护网络路径的要求。

  1. 确认 Broker 的精确方法,MQTT 版本选 5.0。
  2. 选择与它匹配的 SCRAM-SHA-256 或 SCRAM-SHA-512。
  3. 同时提供用户名和密码;密码可以直接输入,也可以来自有效的 SecretRef。
  4. 测试当前表单,再连接已保存的 Client,独立验证消息权限。
Mqttable Client 使用 MQTT 5 和 SCRAM-SHA-256。

密码用于本地计算 SCRAM proof;用户名和密码都必须提供。

Mqttable Client 使用 SCRAM-SHA-512,与本实验切换后的认证器匹配。

只修改 Client 不会改变 Broker。本实验先切换隔离 Broker 的认证配置,再验证这个变体。

每一步交换,不都是“认证成功”

SCRAM 的 client-first 发送初始身份与 nonce 数据;server-first 提供服务端 nonce、salt 和迭代次数;client-final 证明客户端掌握密码派生材料;server-final 携带服务端签名。具体计算由 SCRAM 机制定义,MQTT 负责承载这些消息。机制依据见 RFC 5802和 SCRAM-SHA-256 的 RFC 7677。

示意图对照普通 CONNECT 认证和 MQTT 5 SCRAM 挑战响应交换。

这是协议示意图。初始认证以成功的 CONNACK 完成;AUTH 0x18 表示仍需交换认证数据。

Authentication Method 和 Authentication Data 是属性,不是 AUTH payload;AUTH 没有 payload。方法名必须与 CONNECT 保持一致。AUTH 0x00 表示认证成功,0x18 表示继续认证,0x19 是客户端在连接建立后发起重新认证。初始连接认证和重新认证是两条不同的流程。

协议允许重新认证,不代表本文已验证 Mqttable 提供自动或手动重新认证控件。这个下拉也没有宣称包含 SCRAM-SHA-256-PLUS 等 channel binding 变体。

SHA-1 是兼容选项,不是自动回退

Mqttable 提供 SCRAM-SHA-1。仅在明确的旧系统要求下使用;新部署有选择时,优先采用双方支持的 SHA-256 或 SHA-512。不能因为较强方法连接失败,就自行降级。

Mqttable 选择 SCRAM-SHA-1,被本地 EMQX 认证器拒绝。

本实验 Broker 不支持 SHA-1。这不是 SHA-1 成功交换,也不能据此认定客户端实现出错。

Edit Client:保留、替换与清除凭据

编辑已保存的客户端,不等于重新录入密码。明文密码模式中的 Password saved · Kept unless replaced 表示保留已有密码,但不显示它的原文。

Mqttable 编辑 Client,显示已保存密码的保留状态、Replace 和 Clear on save 操作。

已保存密码不是空凭据。界面有意不回显原文。

  • 保留: 不修改已有凭据。进入替换模式后,输入留空且未选 Clear on save,也保留原值。
  • 替换: 点击 Replace,输入新密码或 Token,在 Client 断开时测试当前表单,再保存。测试成功本身不会保存替换值。
  • 清除: 明确选择 Clear on save 后保存。如果当前方式要求凭据,表单可能拒绝这个空状态;保存被拒绝不等于凭据已经清除。
  • Saved SecretRef: 选择正确引用。这是另一种凭据来源,不是额外的一层认证。切回 Raw password 时,不能假设引用中的原文会自动复制或显示。
  • None: 当前连接不再使用密码,不等于删除已保存的 Secret 或引用。

验证变更前先断开 Client。已经在线时,Mqttable 显示真实运行状态,并提示当前编辑未验证;草稿不能证明现有连接已使用新设置。完整保存与连接流程见 Broker 与连接。

怎样分别证明认证成功和 Topic 有权限

Broker 测试连接使用端点及当前传输、TLS 设置,不发送 New Client 的用户名和密码。这个探测被拒绝认证,本身不能证明 TLS 证书错误。

Client 测试连接不保存配置,测试完整当前表单。它可以使用尚未保存的密码或 Token、保留的旧凭据、SecretRef,以及 SCRAM 选择。成功后临时连接立即断开,不是持久的 Connected Client,也不验证消息权限。

测试使用真实 Client ID 和会话设置。同身份的其他客户端可能被断开,它的 Will 可能被触发,会话状态也可能受影响。Mqttable 会阻止已知本地在线身份的测试,但不能排除未知外部客户端。本实验使用专门 Client ID,不配置 Will,不使用有价值的持久会话。

对每个认证成功的方法,再做一次消息验证:

  1. 保存并连接 Client,确认 Connected,并在可见时检查成功的 CONNACK。
  2. 订阅明确允许的 mqttable/auth/lab/<method>,QoS 1,关闭 No Local。
  3. 等待成功 SUBACK,再发布。
  4. 向同一 Topic 发布少量合成 JSON,QoS 1,Retain 关闭。
  5. 找到 Topic、payload 相同的发送与接收 PUBLISH。单独看到 PUBACK,不能证明订阅者收到消息。
Mqttable 认证成功后完成订阅,显示 Topic 和 payload 相同的发送与接收 PUBLISH。

消息闭环比 CONNECT 多证明了一步,但不能证明 TLS、其他 Topic 的权限或生产可用性。

本实验的 Mosquitto 密码 Listener 拒绝另一个 Topic:mqttable/auth/denied。保持同一组有效凭据,订阅这个 Topic,在这版 Mosquitto 配置中会得到成功 SUBACK;但以 QoS 1 发布,收到的是 PUBACK 0x87(Not authorized)。订阅回执不是发布权限的授权,读取 ACL 也仍可过滤投递。截图展示的是已经通过认证、但发布操作没有授权的结果。

已经通过认证的 Mqttable Client 向 ACL 范围外 Topic 发布,收到 PUBACK Not authorized。

保留已接受的身份,检查它的 ACL。这个失败不应先靠替换正确密码处理。

按失败阶段排障

左右滚动表格,查看其余列。

观察到的结果下一步检查
表单提示 JWT Token 或 SCRAM 凭据必填凭据来源、SecretRef 是否有效、用户名与密码、MQTT 版本;此时还没进行网络认证
CONNACK 之前 TLS 握手失败Host、Listener 协议、CA 信任、证书名称和客户端证书要求
MQTT 5 CONNACK 0x86,Bad User Name or Password账号、密码或 Token、JWT 签名与 claims、认证器状态及 Listener 策略
CONNACK 0x87,Not authorized连接权限和认证链;结果本身不能定位到某条 ACL
CONNACK 0x8C,Bad authentication method精确增强认证方法和 Broker 支持范围
Connected 之后 SUBACK 或 PUBACK 拒绝操作Topic、操作类型、QoS/Retain 限制和已认证主体的授权规则
Mqttable Client 测试连接使用故意输错的本地密码,得到失败结果。

本负例只改变了一项凭据。其他部署出现通用拒绝时,仍需要自己的证据。

不要假定所有 Broker 都返回相同的详细原因。MQTT 标准允许在多条路径拒绝或关闭连接,运营方也可能有意隐藏凭据失败的细节。记录版本、Listener、方法、报文方向和实际结果,不把密码、Token 或私钥复制到工单。认证方式指南提供更短的配置参考。

JWT 必须使用 MQTT 5 AUTH 吗?

不必须。放进 CONNECT Password 的 Token 是基础凭据传递,Broker 支持时也可以用于 MQTT 3.1.1。增强认证机制可以定义 Token 交换,但 Mqttable 这里的 JWT 模式不是那种流程。

SCRAM 能让明文 MQTT 安全地跨公网吗?

不能。不把原始密码放进 CONNECT Password,不等于保护整条连接。使用正确验证身份的 TLS 或经过明确评估的受保护传输,也不要把本文 loopback Listener 直接开放到公网。

认证测试通过,为什么发布失败?

Broker 接受了连接身份,仍可能拒绝 Topic 操作。检查 SUBACK、PUBACK 和 ACL,再在允许的 Topic 上复跑发布与订阅流程。

能解码 JWT 就表示有效吗?Mqttable 会续期吗?

解码只显示结构,接受与否仍取决于签名和 claims 策略。这个连接路径不自动获取或刷新 Token。按签发方授权流程取得新 Token,替换后重新测试草稿。

可以复制实验签名密钥和截图凭据到生产吗?

不可以。实验材料都是一次性的。按运营方策略创建凭据,把签名密钥留在正确的签发边界,泄露后及时轮换。先确定 Broker 要求的机制,再证明连接成功,最后证明你实际需要的 Topic 操作。