v3.1.1
其他资源
网站
测试工具
基本概念 Basic Conception
Session 会话
定义
- 定义:某个客户端(由ClientID作为标识)和某个服务器之间的逻辑层面的通信
ClientID
- 只能包含这些 大写字母,小写字母 和 数字(0-9a-zA-Z),23个字符以内
- 如果 ClientID 在多次 TCP连接中保持一致,客户端和服务器端会保留会话信息(Session)
- 同一时间内 Server 和同一个 ClientID 只能保持一个 TCP 连接,再次连接会踢掉前一个
CleanSession 标记
- 0 —— 开启会话重用机制。网络断开重连后,恢复之前的Session信息。需要客户端和服务器有相关Session持久化机制。
- 1 —— 关闭会话重用机制。每次Connect都是一个新Session,会话仅持续和网络连接同样长的时间。
客户端 Session
- 已经发送给服务端,但是还没有完成确认的 QoS 1 和 QoS 2 级别的消息
- 已从服务端接收,但是还没有完成确认的 QoS 2 级别的消息
服务器端 Session
- 会话是否存在,即使会话状态的其它部分都是空 (SessionFlag)
- 客户端的订阅信息 (ClientSubcription)
- 已经发送给客户端,但是还没有完成确认的 QoS 1 和 QoS 2 级别的消息
- 即将传输给客户端的 QoS 1 和 QoS 2 级别的消息
- 已从客户端接收,但是还没有完成确认的 QoS 2 级别的消息
- (可选)准备发送给客户端的 QoS 0 级别的消息
长连接维护与管理
Keep Alive 心跳
- 目的是保持长连接的可靠性,以及双方对彼此是否在线的确认。
- 客户端在Connect的时候设置 Keep Alive 时长。如果服务端在 1.5 * KeepAlive 时间内没有收到客户端的报文,它必须断开客户端的网络连接
- Keep Alive 的值由具体应用指定,一般是几分钟。允许的最大值是 18 小时 12 分 15 秒
Will 遗嘱
- 遗嘱消息(Will Message)存储在服务端,当网络连接关闭时,服务端必须发布这个遗嘱消息,所以被形象地称之为遗嘱,可用于通知异常断线。
- 客户端发送 DISCONNECT 关闭链接,遗嘱失效并删除
- 客户端在保持连接(Keep Alive)的时间内未能通讯
- 客户端没有先发送 DISCONNECT 报文直接关闭了网络连接
- 0 -- 关闭遗嘱功能,Will QoS 和 Will Retain 必须为 0
- 1 -- 开启遗嘱功能,需要设置 Will Retain 和 Will QoS
消息基本概念
报文标识 Packet Identifier
- 存在报文的可变报头部分,非零两个字节整数 (0-65535]
- 一个流程中重复:这些报文包含 PacketID,而且在一次通信流程内保持一致:
- PUBLISH(QoS>0 时),PUBACK,PUBREC,PUBREL,PUBCOMP
- 新的不重复:客户端每次发送一个新的这些类型的报文时都必须分配一个当前 未使用的PacketID
- 当客户端处理完这个报文对应的确认后,这个报文标识符就释放可重用。
- 独立维护:客户端和服务端彼此独立地分配报文标识符。因此,客户端服务端组合使用相同的报文标识符可以实现 并发 的消息交换。可能出现一下情况,并不算异常:
Payload 有效载荷,消息体
- Publish 的 Payload 允许为空。在很多场合下,代表将持久消息(或者遗嘱消息)清空。
Retain 持久消息(粘性消息)
- RETAIN 标记:每个Publish消息都需要指定的标记
- 0 —— 服务端不能存储这个消息,也不能移除或替换任何 现存的保留消息
- 1 —— 服务端必须存储这个应用消息和它的QoS等级,以便它可以被分发给未来的订阅者
- 每个Topic只会保留最多一个 Retain 持久消息
- 客户端订阅带有持久消息的Topic,会立即受到这条消息
- 服务器可以选择丢弃持久消息,比如内存或者存储吃紧的时候
- 如果客户端想要删除某个Topic 上面的持久消息,可以向这个Topic发送一个Payload为空的持久消息
QoS 服务等级(消息可靠性)
最多一次 At most Once(QoS == 0)
- 没有回复,不需要存储。有可能丢失(网络异常断开,业务层繁忙或者错误)
至少一次 At least Once(QoS == 1 )
- 发送者S 发送前需要做持久化存储,接受者R 不需要持久化存储
- 如果 发送者S 没有收到 接收者R 的回复 PUBACK,过一段时间 发送者S 会重新发送,DUP标记为1(在同一Session内)。
- 接受者R 发送 PUBACK 后,不需要知道对方是否收到,马上把消息交给上层业务。如果此时网络异常,会导致发送者重发。这样接受者收到多个消息(所以叫至少一次)。
有且仅有一次 Exactly Once(QoS == 2 )
- 发送者S 发送 PUBLISH 前,需要做持久化存储。接受者R 回复PUBREC 后,也需要做持久化存储
- 如果 发送者S 没有收到 接收者R 的回复 PUBREC,过一段时间 发送者S 会重新发送,DUP标记为1(在同一Session内)。
- 如果 接受者R 没有收到 发送者S 的回复 PUBREL,过一段时间 接受者R 会重新发送PUBREC。
- 发送者S 收到 PUBREC后,删除持久化消息,但是要保存 PacketID
- 接收者R 受到 PUBREL后,删除持久化PUBREC。然后将消息发给上层,同时回复 PUBCOMP。
- 发送者S 收到 PUBCOMP 后,删除 PacketID,通信完美结束。
- 这套流程可以 严格保证 一个包不管在什么情况下 接收者R 只收到一次 。
重传标记 DUP 与重传机制 (QoS > 0)
- 如果客户端或者服务器发送了一个 Publish 消息,一段时间内没收到 PublishAck 回复,则认为消息丢失,进行重传。
- 在一个Session内,进行重传的时候,头部的 DUP 重传标志 设置为1。
- 客户端有可能收到 DUP == 0 的重传包(Payload相同,PacketID不同)。因为可能因为网络问题,下次重传时间较久,Session已经释放,PacketID 已经变更。
- 客户端发给服务器的和服务器转发给别的客户端的 Publish 消息,DUP 重传标志不会传递
- 接收者收到一个 DUP 标志为 1 的控制报文时,并不能保证之前收到过相同的报文
消息重传顺序
- 重发任何之前的 PUBLISH 报文时,必须按原始 PUBLISH 报文的发送顺序重发 (适用于QoS 1 和 QoS 2 消息)
- 必须按照对应的 PUBLISH 报文的顺序发送 PUBACK 报文 (QoS 1 消息)
- 必须按照对应的 PUBLISH 报文的顺序发送 PUBREC 报文 (QoS 2 消息)
- 必须按照对应的 PUBREC 报文的顺序发送 PUBREL 报文 (QoS 2 消息)
- QoS == 1 时,虽然是PUBLISH有序的,但是可能会重复。例如,发布者按顺序 1,2,3,4 发送消息,订阅者收到的顺序可能是 1,2,3,2,3,4。
- QoS == 1 时,如果限制 传输窗口 (in-flight window) ==1,即同一时刻只有一个包在传输,就可以保证乱序。例如,订阅者收到的顺序可能是 1,2,3,3,4,而不是 1,2,3,2,3,4
话题 与订阅机制 Topic & Subcribe
Topic 话题 和 TopicFilter 话题过滤器
- UTF-8 编码字符串,不能超过 65535 字节。层级数量没有限制
- 不能包含任何的下文中提到的特殊符号(/、+、#),必须至少包含一个字符
- 区分大小写,可以包含空格,不能包含空字符 (Unicode U+0000)
- 在收部或尾部增加 斜杠 “/”,会产生不同的Topic和TopicFilter。举例:
- 只包含斜杠 “/” 的 Topic 或 TopicFilter 是合法的
TopicFilter中的特殊符号
- 主题层级分隔符可以出现在 Topic 或 TopicFilter 的任何位置
- 特例:相邻的主题层次分隔符表示一个零长度的主题层级
- 只能用于单个主题层级匹配的通配符。例如,“a/b/+” 匹配 “a/b/c1” 和 “a/b/c2” ,但是不匹配 “a/b/c/d”
- 可以匹配 任意层级,包括第一个和最后一个层级。例如,“+” 是有效的,“sport/+/player1” 也是有效的。
- 可以在多个层级中使用它,也可以和多层通配符一起使用。 例如,“+/tennis/#” 是有效的。
- 只能匹配本级不能匹配上级。例如,“sport/+” 不匹配 “sport” 但是却匹配“sport/”,“/finance” 匹配 “+/+” 和 “/+” ,但是不匹配 “+”。
- 匹配包含本身的层级和子层级。例如 “a/b/c/#" 可以匹配 “a/b/c”、“a/b/c/d” 和 “a/b/c/d/e”
- 必须是最后的结尾。例如“sport/tennis/#/ranking”是无效的
- “#”是有效的,会收到所有的应用消息。 (服务器端应将此类 TopicFilter禁掉 )
以$开头的,服务器保留
- 服务端不能将 $ 字符开头的 Topic 匹配通配符 (#或+) 开头的 TopicFilter
- 服务端应该阻止客户端使用这种 Topic 与其它客户端交换消息。服务端实现可以将 $ 开头的主题名用作其他目的。
- $SYS/ 被广泛用作包含服务器特定信息或控制接口的主题的前缀
- 客户端不特意订阅 $开头的 Topic,就不会收到对应的消息
- 订阅 “#” 的客户端不会收到任何发布到以 “$” 开头主题的消息
- 订阅 “+/A/B” 的客户端不会收到任何发布到 “$SYS/A/B” 的消息
- 订阅 “$SYS/#” 的客户端会收到发布到以 “$SYS/” 开头主题的消息
- 订阅 “$SYS/A/+” 的客户端会收到发布到 “$SYS/A/B” 主题的消息
- 如果客户端想同时接受以 “$SYS/” 开头主题的消息和不以 $ 开头主题的消息,它需要同时 订阅 “#” 和 “$SYS/#”
订阅 Subscribe 与 QoS降级
- 一个Subsribe请求 可订阅多个 Topic(节省带宽,多订阅尽量用一次请求)。取消订阅也同理
- 每一个订阅需要指定一个QoS,指定了客户端接收消息所允许的最大QoS级别。但是服务器端最终授权返回的QoS可能会小于等于客户端请求的QoS
- 对于高于QoS的消息(比如说订阅的QoS限制到1,消息的QoS指定到2),那么客户端会收到一个QoS降低为指定的 限制QoS 的消息(消息的QoS降为1,不保证只收到一次)
- 订阅关系可以被覆盖,以TopicFilter为标识。如果后面订阅一个相同的TopicFilter,但是指定的QoS不同,则以后面的为准,QoS升高后,重发相应等级的 Retain 消息
安全传输与鉴权认证 Security & Certification
传输层
- 可以采用 TCP、SSL/TLS [RFC5246] 、WebSocket 作为传输层。UDP不可以,因为不保证可靠传输与有序传输。
- 服务器端返回的数据极有可能出现 粘包 的情况。客户端经常会在连接建立之后,连续调用多个订阅,这样服务器端就会回复多个订阅ACK包,同时还有各个Topic上的持久消息,一般粘成一个TCP包返回过来
- 8883:over SSL/TLS,单向认证(强烈建议)
潜在的风险与应对机制
- 客户端和服务端的静态数据可以被访问(比如客户端Root导致数据泄露、服务器被拖库)
- 协议规定的行为可能有副作用 (如计时器攻击 “timing attacks”)
- 通信可能会被拦截、修改、重定向或者泄露(抓包、中间人)
客户端身份验证与授权 (Authentication & Authorization of Client)
- 用户名+密码验证:Connect 登录的时候,传入 UserName 和 Password
- 用户名(UserName Flag)标记设置为1,才可以穿入
- 外部验证:LDAP、OAuth 或者 操作系统的认证机制
- 应用层:客户端通过应用消息给服务端发送凭证用于身份验证。
- 授权:基于客户端提供的信息如用户名、客户端标识符(ClientId)、客户端的主机名或 IP 地址,或者身份认证的结果,服务端可以限制对某些服务端资源的访问
服务端身份验证 (Authentication of Server by Client)
- MQTT 协议不是双向信任的,它没有提供客户端验证服务端身份的机制
- TLS:客户端可以使用服务端发送的SSL证书验证服务端的身份
- 应用层:可以通过服务端给客户端发送凭证用于身份验证的应用层消息
- VPN:在客户端和服务端之间使用虚拟专用网(VPN)可以确保客户端连接的是预期的服务器。
控制报文和 Payload 的完整性(Integrity)
- TLS:提供了对网络传输的数据做完整性校验的哈希算法
- 应用层:可以在应用消息中单独包含哈希值。这样做可以为 PUBLISH 控制报文的网络传输和静态数据提供内容的完整性检查
- VPN:在客户端和服务端之间使用虚拟专用网(VPN)连接可以在 VPN 覆盖的网络段提供数据完整性检查
控制报文和 Payload 的保密性(Privacy)
- 轻量级加密:AES or DES,可适用于低端设备
- 应用层:可以单独加密 Payload 内容。这可以提供 Payload 传输途中和静态数据的私密性。但不能给应用消息的其它属性如 Topic 加密
- 静态数据加密:客户端和服务端实现可以加密存储静态数据,例如可以将应用消息作为会话的一部分存储
- VPN:在客户端和服务端之间使用虚拟专用网(VPN)连接可以在 VPN 覆盖的网络段保证数据的私密性
异常行为的检测
- 服务端实现可以监视客户端的行为,检测潜在的安全风险。例如:
- 发现违反安全规则的行为,服务端实现可以断开客户端连接
- 可以基于 IP地址 或 ClientID 实现一个 动态黑名单列表
- 可以使用网络层面的控制,实现基于 IP 地址或其它信息的 速率限制 或黑名单
最佳实践 Best Practice
客户端 Client
选型
服务器端 Server
Broker选型
Benchmark
分布式部署 Cluster
TopicTree 设计
参考
参考 Reference
v3.1.1
其他资源
网站
测试工具
基本概念 Basic Conception
Session 会话
定义
- 定义:某个客户端(由ClientID作为标识)和某个服务器之间的逻辑层面的通信
ClientID
- 只能包含这些 大写字母,小写字母 和 数字(0-9a-zA-Z),23个字符以内
- 如果 ClientID 在多次 TCP连接中保持一致,客户端和服务器端会保留会话信息(Session)
- 同一时间内 Server 和同一个 ClientID 只能保持一个 TCP 连接,再次连接会踢掉前一个
CleanSession 标记
- 0 —— 开启会话重用机制。网络断开重连后,恢复之前的Session信息。需要客户端和服务器有相关Session持久化机制。
- 1 —— 关闭会话重用机制。每次Connect都是一个新Session,会话仅持续和网络连接同样长的时间。
客户端 Session
- 已经发送给服务端,但是还没有完成确认的 QoS 1 和 QoS 2 级别的消息
- 已从服务端接收,但是还没有完成确认的 QoS 2 级别的消息
服务器端 Session
- 会话是否存在,即使会话状态的其它部分都是空 (SessionFlag)
- 客户端的订阅信息 (ClientSubcription)
- 已经发送给客户端,但是还没有完成确认的 QoS 1 和 QoS 2 级别的消息
- 即将传输给客户端的 QoS 1 和 QoS 2 级别的消息
- 已从客户端接收,但是还没有完成确认的 QoS 2 级别的消息
- (可选)准备发送给客户端的 QoS 0 级别的消息
长连接维护与管理
Keep Alive 心跳
- 目的是保持长连接的可靠性,以及双方对彼此是否在线的确认。
- 客户端在Connect的时候设置 Keep Alive 时长。如果服务端在 1.5 * KeepAlive 时间内没有收到客户端的报文,它必须断开客户端的网络连接
- Keep Alive 的值由具体应用指定,一般是几分钟。允许的最大值是 18 小时 12 分 15 秒
Will 遗嘱
- 遗嘱消息(Will Message)存储在服务端,当网络连接关闭时,服务端必须发布这个遗嘱消息,所以被形象地称之为遗嘱,可用于通知异常断线。
- 客户端发送 DISCONNECT 关闭链接,遗嘱失效并删除
- 客户端在保持连接(Keep Alive)的时间内未能通讯
- 客户端没有先发送 DISCONNECT 报文直接关闭了网络连接
- 0 -- 关闭遗嘱功能,Will QoS 和 Will Retain 必须为 0
- 1 -- 开启遗嘱功能,需要设置 Will Retain 和 Will QoS
消息基本概念
报文标识 Packet Identifier
- 存在报文的可变报头部分,非零两个字节整数 (0-65535]
- 一个流程中重复:这些报文包含 PacketID,而且在一次通信流程内保持一致:
- PUBLISH(QoS>0 时),PUBACK,PUBREC,PUBREL,PUBCOMP
- 新的不重复:客户端每次发送一个新的这些类型的报文时都必须分配一个当前 未使用的PacketID
- 当客户端处理完这个报文对应的确认后,这个报文标识符就释放可重用。
- 独立维护:客户端和服务端彼此独立地分配报文标识符。因此,客户端服务端组合使用相同的报文标识符可以实现 并发 的消息交换。可能出现一下情况,并不算异常:
Payload 有效载荷,消息体
- Publish 的 Payload 允许为空。在很多场合下,代表将持久消息(或者遗嘱消息)清空。
Retain 持久消息(粘性消息)
- RETAIN 标记:每个Publish消息都需要指定的标记
- 0 —— 服务端不能存储这个消息,也不能移除或替换任何 现存的保留消息
- 1 —— 服务端必须存储这个应用消息和它的QoS等级,以便它可以被分发给未来的订阅者
- 每个Topic只会保留最多一个 Retain 持久消息
- 客户端订阅带有持久消息的Topic,会立即受到这条消息
- 服务器可以选择丢弃持久消息,比如内存或者存储吃紧的时候
- 如果客户端想要删除某个Topic 上面的持久消息,可以向这个Topic发送一个Payload为空的持久消息
QoS 服务等级(消息可靠性)
最多一次 At most Once(QoS == 0)
- 没有回复,不需要存储。有可能丢失(网络异常断开,业务层繁忙或者错误)
至少一次 At least Once(QoS == 1 )
- 发送者S 发送前需要做持久化存储,接受者R 不需要持久化存储
- 如果 发送者S 没有收到 接收者R 的回复 PUBACK,过一段时间 发送者S 会重新发送,DUP标记为1(在同一Session内)。
- 接受者R 发送 PUBACK 后,不需要知道对方是否收到,马上把消息交给上层业务。如果此时网络异常,会导致发送者重发。这样接受者收到多个消息(所以叫至少一次)。
有且仅有一次 Exactly Once(QoS == 2 )
- 发送者S 发送 PUBLISH 前,需要做持久化存储。接受者R 回复PUBREC 后,也需要做持久化存储
- 如果 发送者S 没有收到 接收者R 的回复 PUBREC,过一段时间 发送者S 会重新发送,DUP标记为1(在同一Session内)。
- 如果 接受者R 没有收到 发送者S 的回复 PUBREL,过一段时间 接受者R 会重新发送PUBREC。
- 发送者S 收到 PUBREC后,删除持久化消息,但是要保存 PacketID
- 接收者R 受到 PUBREL后,删除持久化PUBREC。然后将消息发给上层,同时回复 PUBCOMP。
- 发送者S 收到 PUBCOMP 后,删除 PacketID,通信完美结束。
- 这套流程可以 严格保证 一个包不管在什么情况下 接收者R 只收到一次 。
重传标记 DUP 与重传机制 (QoS > 0)
- 如果客户端或者服务器发送了一个 Publish 消息,一段时间内没收到 PublishAck 回复,则认为消息丢失,进行重传。
- 在一个Session内,进行重传的时候,头部的 DUP 重传标志 设置为1。
- 客户端有可能收到 DUP == 0 的重传包(Payload相同,PacketID不同)。因为可能因为网络问题,下次重传时间较久,Session已经释放,PacketID 已经变更。
- 客户端发给服务器的和服务器转发给别的客户端的 Publish 消息,DUP 重传标志不会传递
- 接收者收到一个 DUP 标志为 1 的控制报文时,并不能保证之前收到过相同的报文
消息重传顺序
- 重发任何之前的 PUBLISH 报文时,必须按原始 PUBLISH 报文的发送顺序重发 (适用于QoS 1 和 QoS 2 消息)
- 必须按照对应的 PUBLISH 报文的顺序发送 PUBACK 报文 (QoS 1 消息)
- 必须按照对应的 PUBLISH 报文的顺序发送 PUBREC 报文 (QoS 2 消息)
- 必须按照对应的 PUBREC 报文的顺序发送 PUBREL 报文 (QoS 2 消息)
- QoS == 1 时,虽然是PUBLISH有序的,但是可能会重复。例如,发布者按顺序 1,2,3,4 发送消息,订阅者收到的顺序可能是 1,2,3,2,3,4。
- QoS == 1 时,如果限制 传输窗口 (in-flight window) ==1,即同一时刻只有一个包在传输,就可以保证乱序。例如,订阅者收到的顺序可能是 1,2,3,3,4,而不是 1,2,3,2,3,4
话题 与订阅机制 Topic & Subcribe
Topic 话题 和 TopicFilter 话题过滤器
- UTF-8 编码字符串,不能超过 65535 字节。层级数量没有限制
- 不能包含任何的下文中提到的特殊符号(/、+、#),必须至少包含一个字符
- 区分大小写,可以包含空格,不能包含空字符 (Unicode U+0000)
- 在收部或尾部增加 斜杠 “/”,会产生不同的Topic和TopicFilter。举例:
- 只包含斜杠 “/” 的 Topic 或 TopicFilter 是合法的
TopicFilter中的特殊符号
- 主题层级分隔符可以出现在 Topic 或 TopicFilter 的任何位置
- 特例:相邻的主题层次分隔符表示一个零长度的主题层级
- 只能用于单个主题层级匹配的通配符。例如,“a/b/+” 匹配 “a/b/c1” 和 “a/b/c2” ,但是不匹配 “a/b/c/d”
- 可以匹配 任意层级,包括第一个和最后一个层级。例如,“+” 是有效的,“sport/+/player1” 也是有效的。
- 可以在多个层级中使用它,也可以和多层通配符一起使用。 例如,“+/tennis/#” 是有效的。
- 只能匹配本级不能匹配上级。例如,“sport/+” 不匹配 “sport” 但是却匹配“sport/”,“/finance” 匹配 “+/+” 和 “/+” ,但是不匹配 “+”。
- 匹配包含本身的层级和子层级。例如 “a/b/c/#" 可以匹配 “a/b/c”、“a/b/c/d” 和 “a/b/c/d/e”
- 必须是最后的结尾。例如“sport/tennis/#/ranking”是无效的
- “#”是有效的,会收到所有的应用消息。 (服务器端应将此类 TopicFilter禁掉 )
以$开头的,服务器保留
- 服务端不能将 $ 字符开头的 Topic 匹配通配符 (#或+) 开头的 TopicFilter
- 服务端应该阻止客户端使用这种 Topic 与其它客户端交换消息。服务端实现可以将 $ 开头的主题名用作其他目的。
- $SYS/ 被广泛用作包含服务器特定信息或控制接口的主题的前缀
- 客户端不特意订阅 $开头的 Topic,就不会收到对应的消息
- 订阅 “#” 的客户端不会收到任何发布到以 “$” 开头主题的消息
- 订阅 “+/A/B” 的客户端不会收到任何发布到 “$SYS/A/B” 的消息
- 订阅 “$SYS/#” 的客户端会收到发布到以 “$SYS/” 开头主题的消息
- 订阅 “$SYS/A/+” 的客户端会收到发布到 “$SYS/A/B” 主题的消息
- 如果客户端想同时接受以 “$SYS/” 开头主题的消息和不以 $ 开头主题的消息,它需要同时 订阅 “#” 和 “$SYS/#”
订阅 Subscribe 与 QoS降级
- 一个Subsribe请求 可订阅多个 Topic(节省带宽,多订阅尽量用一次请求)。取消订阅也同理
- 每一个订阅需要指定一个QoS,指定了客户端接收消息所允许的最大QoS级别。但是服务器端最终授权返回的QoS可能会小于等于客户端请求的QoS
- 对于高于QoS的消息(比如说订阅的QoS限制到1,消息的QoS指定到2),那么客户端会收到一个QoS降低为指定的 限制QoS 的消息(消息的QoS降为1,不保证只收到一次)
- 订阅关系可以被覆盖,以TopicFilter为标识。如果后面订阅一个相同的TopicFilter,但是指定的QoS不同,则以后面的为准,QoS升高后,重发相应等级的 Retain 消息
安全传输与鉴权认证 Security & Certification
传输层
- 可以采用 TCP、SSL/TLS [RFC5246] 、WebSocket 作为传输层。UDP不可以,因为不保证可靠传输与有序传输。
- 服务器端返回的数据极有可能出现 粘包 的情况。客户端经常会在连接建立之后,连续调用多个订阅,这样服务器端就会回复多个订阅ACK包,同时还有各个Topic上的持久消息,一般粘成一个TCP包返回过来
- 8883:over SSL/TLS,单向认证(强烈建议)
潜在的风险与应对机制
- 客户端和服务端的静态数据可以被访问(比如客户端Root导致数据泄露、服务器被拖库)
- 协议规定的行为可能有副作用 (如计时器攻击 “timing attacks”)
- 通信可能会被拦截、修改、重定向或者泄露(抓包、中间人)
客户端身份验证与授权 (Authentication & Authorization of Client)
- 用户名+密码验证:Connect 登录的时候,传入 UserName 和 Password
- 用户名(UserName Flag)标记设置为1,才可以穿入
- 外部验证:LDAP、OAuth 或者 操作系统的认证机制
- 应用层:客户端通过应用消息给服务端发送凭证用于身份验证。
- 授权:基于客户端提供的信息如用户名、客户端标识符(ClientId)、客户端的主机名或 IP 地址,或者身份认证的结果,服务端可以限制对某些服务端资源的访问
服务端身份验证 (Authentication of Server by Client)
- MQTT 协议不是双向信任的,它没有提供客户端验证服务端身份的机制
- TLS:客户端可以使用服务端发送的SSL证书验证服务端的身份
- 应用层:可以通过服务端给客户端发送凭证用于身份验证的应用层消息
- VPN:在客户端和服务端之间使用虚拟专用网(VPN)可以确保客户端连接的是预期的服务器。
控制报文和 Payload 的完整性(Integrity)
- TLS:提供了对网络传输的数据做完整性校验的哈希算法
- 应用层:可以在应用消息中单独包含哈希值。这样做可以为 PUBLISH 控制报文的网络传输和静态数据提供内容的完整性检查
- VPN:在客户端和服务端之间使用虚拟专用网(VPN)连接可以在 VPN 覆盖的网络段提供数据完整性检查
控制报文和 Payload 的保密性(Privacy)
- 轻量级加密:AES or DES,可适用于低端设备
- 应用层:可以单独加密 Payload 内容。这可以提供 Payload 传输途中和静态数据的私密性。但不能给应用消息的其它属性如 Topic 加密
- 静态数据加密:客户端和服务端实现可以加密存储静态数据,例如可以将应用消息作为会话的一部分存储
- VPN:在客户端和服务端之间使用虚拟专用网(VPN)连接可以在 VPN 覆盖的网络段保证数据的私密性
异常行为的检测
- 服务端实现可以监视客户端的行为,检测潜在的安全风险。例如:
- 发现违反安全规则的行为,服务端实现可以断开客户端连接
- 可以基于 IP地址 或 ClientID 实现一个 动态黑名单列表
- 可以使用网络层面的控制,实现基于 IP 地址或其它信息的 速率限制 或黑名单
最佳实践 Best Practice
客户端 Client
选型
服务器端 Server
Broker选型
Benchmark
分布式部署 Cluster
TopicTree 设计
参考