《MQTT安全系列》将分为上下两集介绍最广泛使用的物联网协议之一—MQTT。上集主讲MQTT概念、下集讲MQTT主要的安全问题。本文我们先讲一下MQTT到底是什么?
0xA 介绍
消息队列遥测传输(MQTT)是一种ISO标准消息传递协议,旨在通过不可靠的网络连接传感器。和其他物联网协议一样,它是轻量级的,便于应用及编程实现,适用于M2M及物联网等自动化环境。它是一种应用层协议,可运行在基于TCP/IP、以及可靠、有序和双向通信的网络协议(如websockets)上。
端口号 |
通信 |
1883 |
Plain text |
8883 |
TLS |
MQTT详细的协议规范可以在这里找到:
https://docs.oasis-open.org/mqtt/mqtt/v5.0/mqtt-v5.0.html
0xA1 应用
由于MQTT协议具有轻量级开发、灵活的体系架构和丰富的云端通信延展等特点。该协议在工业物联网(IIoT)、工业控制系统(ICS)、企业物联网(IoT)等领域获得了广泛的应用。我们认为MQTT在不久的将来会为智慧城市、智能家居及工业互联网化的落地提供重要的通信支持!
0xA2 架构
MQTT网络中心叫MQTT消息服务器(Broker,后边我将称作代理)。MQTT节点都连接到该代理。每个节点可以订阅和发布消息。而代理基于订阅内容将消息转发到其他节点。
在整个物联网生态中,哪些组件将充当消息代理还是节点完全取决于基础设施、生态系统和实施的要求。
节点通常是(不限于):
一个物联网终端
一个物联网的网关
应用服务器
移动应用服务器
云应用服务器
其他MQTT broker(当桥接时)
同样的,MQTT broker通常是:
一个物联网的网关
云端MQTT服务器
移动应用
0xB MQTT通信
0xB1 TOPIC
多个节点通过订阅同一主题进行相互通信,它们通过topic来指定发送目标及获取消息来源。我们分别来介绍一下mqtt topic的重要概念:
Topic:mqtt消息的标签,应用于该主题的节点可以相互通信。主题名称的语法类
似于目录结构。(例如,某品牌汽车锁车协议主题 remote_control/result/rvs_set_doorlock)
Topic Level:主题级别,主题中也有类似于目录结构中的级别概念。
例如:remote_control/result/rvs_set_doorlock 中result 为rvs_set_doorlock的父级。
Topic Filter:主题过滤器,mqtt开发支持规则过滤,节点可以向MQTT代理订
阅单个请求中的一个或多个主题,并且利用规则来匹配实现指定的逻辑(类似正则表达式)。下面给大家介绍三个重要的通配符:
主题层级分隔符“/“:用于分割主题层级,/分割后的主题,这是消息主题层级设计中很重要的符号。比方说:aaa/bbb和aaa/bbb/ccc和aaa/bbb/ccc/ddd,这样的消息主题格式,是一个层层递进的关系,可通过多层通配符同时匹配两者,或者单层通配符只匹配一个。这在现实场景中,可以应用到:公司的部门层级推送、国家城市层级推送等包含层级关系的场景。
单层通配符“+“:单层通配符只能匹配一层主题。比如:aaa/+可以匹配aaa/bbb,但是不能匹配aaa/bbb/ccc。单独的+号可以匹配单层的所有推送。
多层通配符#:多层通配符可以匹配于多层主题。比如:aaa/#不但可以匹配aaa/bbb,还可以匹配aaa/bbb/ccc/ddd。也就是说,多层通配符可以匹配符合通配符之前主题层级的所有子集主题。单独的#匹配所有的消息主题。
4、Topic with $:以$开头的topic(例如$foo/bar/boo),一般mqtt代理都会使用$开
头的topic作为系统调试、存储信息专用的topic,所以mqtt代理将不会匹配这样的topic信息,开发人员也不宜使用$开头的topic进行应用层的通信。
0xB2 通信案例
下面的示例演示了一个场景,其中有4个节点连接到MQTT代理。三个节点(Node1、Node2和Node3)使用不同的主题过滤器订阅他们选择的主题。然后,一个节点(Node1)向mqtt代理发布主题消息。在三个订阅节点中,代理只将消息发布到两个节点(Node2和Node3),因为它们的主题过滤器的结论与发布消息的主题匹配。
三个节点根据规则关注不同主题
节点4发送topic为a的消息
只有节点2、3根据规则可以接收到信息
0xC MQTT协议
我们在这里讨论mqtt通讯协议的内部结构。
0xC1 MQTT控制包组成
节点和代理之间的通信通过MQTT控制包进行。MQTT控制数据包由三个部分组成:
固定头(Fixed header)。存在于所有MQTT数据包中,表示数据包类型及数据包的分组类标识。
可变头(Variable header)。存在于部分MQTT数据包中,数据包类型决定了可变头是否存在及其具体内容。
消息体(Payload)。存在于部分MQTT数据包中,表示客户端收到的具体内容。
MQTT 5.0规范中定义了16个MQTT控制数据包。
封包类别 |
描述 |
Reserved |
保留 |
CONNECT |
链接请求 |
CONNACK |
链接 |
PUBLISH |
发布消息 |
PUBACK |
发布消息确认 (QoS = 1) |
PUBREC |
发布消息已接收 (QoS = 2) |
PUBREL |
发布版本 (QoS = 2) |
PUBCOMP |
发布完成 (QoS = 2) |
SUBSCRIBE |
订阅请求 |
SUBACK |
订阅确认 |
UNSUBSCRIBE |
取消订阅请求 |
UNSUBACK |
取消订阅确认 |
PINGREQ |
Ping 请求 |
PINGRESP |
Ping 回应 |
DISCONNECT |
链接断开通知 |
AUTH |
身份认证 |
0xC2 Quality of Service (QoS)
协议通过服务质量标记定义了三个通信可靠性级别。标记的值确定发送方和接收方之间的通信将如何进行。需要注意的是,无论是节点和代理都可以充当发送方(Sender)或接收方(Receiver)。 也就是说,QoS 是消息的发送方(Sender)和接受方(Receiver)之间达成的一个协议:
QoS0 代表,Sender 发送的一条消息,Receiver 最多能收到一次,也就是说 Sender 尽
力向 Receiver 发送消息,如果发送失败,也就算了;
发送 |
接收 |
|
发布QoS = 0. 的消息 |
—-> |
接收消息 |
QoS1 代表,Sender 发送的一条消息,Receiver 至少能收到一次,也就是说 Sender 向
Receiver 发送消息,如果发送失败,会继续重试,直到 Receiver 收到消息为止,但是因为重传的原因,Receiver 有可能会收到重复的消息;
发送 |
接收 |
|
发布QoS = 1. 的消息 |
—-> |
接收失败 |
发布QoS = 1. 的消息 |
—-> |
接收失败 |
发布QoS = 1. 的消息 |
—-> |
接收失败 |
发布QoS = 1. 的消息 |
—-> |
接收消息 |
确认发送完毕,结束 |
<—- |
发送返回消息 |
QoS2 代表,Sender 发送的一条消息,Receiver 确保能收到而且只收到一次,也就是
说 Sender 尽力向 Receiver 发送消息,如果发送失败,会继续重试,直到 Receiver 收到消息为止,同时保证 Receiver 不会因为消息重传而收到重复的消息。
注意:
QoS是Sender和Receiver之间的协议,而不是Publisher和Subscriber之间的协议。换句话说,Publisher发布了一条QoS1的消息,只能保证Broker能至少收到一次这个消息;而对于Subscriber能否至少收到一次这个消息,还要取决于Subscriber在Subscibe的时候和Broker协商的QoS等级。
0xC3 MQTT用法
链接(CONNECT)
MQTT连接请求。这是在建立基础连接之后从客户机发送到服务器的第一个MQTT控制数据包。包含一些有关连接的重要信息。包的头部包含一些重要信息:
协议:包括协议名称和版本。
标记:一个字节,用于指定有效负载中是否存在相应的值,如用户名、密码、Will(遗
嘱消息)标志等。当客户端断开连接时,发送给相关的订阅者的遗嘱消息。
Keep-alive: 两个数据包传输需要的时间(以秒为单位),如果超时,客户端必须发送一
个PINGREQ数据包。
属性:变量头的末尾包含规范中定义的标准属性。
而payload中包含如下内容:
客户端ID:连接到服务器的每个节点指定一个唯一的客户端ID。用于维持会话状
态。
用户名:如果包头中标记存在用户名,则payload中此标志为用户名。
密码:如果包头中标记存在密码,则payload中此标志为密码内容。
CONNACK
当Broker收到Client的CONNECT数据包之后,将检查并检验CONNECT数据包的内容,之后回复Client一个CONNACK数据包。CONNACK数据包含连接错误标记、连接确认标记和属性。错误原因标记是一个字节,用于标记连接请求的成功、失败和错误,例如‘Bad User Name or Password’, ‘Protocol Error’, ‘Success’, ‘Malformed Packet’等等。
发布(PUBLISH)
PUBLISH的固定头包含以下内容:
主题名称:要发布消息的主题。
标识符:仅适用于QoS为1或2的数据包,因为需要发送相应的确认数据包。
属性:PUBLISH专用的特定属性。
Payload中包含正在发布的消息内容,这个由开发人员自由决定要交换什么样的数据,如JSON、XML、文本、二进制等。
响应的内容则取决于QoS。
订阅(SUBSCRIBE)
用于订阅一个或多个主题,接收在同一主题上传递的信息。数据包头部包含标识符和属性。payload包含主题过滤器列表,每个主题过滤器后面都有一个描述其订阅选项的公式。
客户端ID:两个字节,用来唯一标识一个数据包,数据包标识只需要保证在从
Sender 到 Receiver 的一次消息交互中保持唯一。
在payload中则:
订阅列表:SUBSCRIBE 的消息体中包含 Client 想要订阅的主题列表,列表中的每
一项由订阅主题名和对应的 QoS 组成。
SUBACK
此数据包是SUBSCRIBE数据包的响应,包含接收和处理SUBSCRIBE的请求。suback可以有选择地同意或拒绝主题过滤器的订阅请求。变量头包含数据包标识符和属性。payload包含订阅原因码的列表,其中每个原因码对应于订阅请求中的相应主题过滤器。原因码是一个字节,用于指定与相应主题筛选器相对应的成功、失败和错误,例‘Not Authorized’, ‘Topic Filter Invalid’, ‘Granted QoS 0’, ‘Wildcard subscriptions not supported’等等。规范中定义了12个订阅原因码。
头部:
客户端ID:两个字节,用来唯一标识一个数据包,数据包标识只需要保证在从
Sender 到 Receiver 的一次消息交互中保持唯一。
Payload中:
原因码:SUBBACK 数据包包含了一组返回码,返回码的数量和顺序和 SUBSCRIBE
数据包的订阅列表对应,用于标识订阅类别中的每一个订阅项的订阅结果。
取消订阅(UNSUBSCRIBE)
此数据包被用于取消订阅一个或多个主题。变量头包含数据包标识符和属性。payload包含客户端想要取消订阅的主题筛选器列表。
头部:
客户端ID:两个字节,用来唯一标识一个数据包,数据包标识只需要保证在从
Sender 到 Receiver 的一次消息交互中保持唯一。
Payload中:
订阅列表:UNSUBSCRIBE 的消息体中包含 Client 想要取消订阅的主题过滤器列
表,这些主题过滤器和 SUBSCRIBE 数据包中一样,可以包含通配符。UNSUBSCRIBE 消息体里面不再包含主题过滤器对应的 QoS 了。
UNSUBACK
此数据包是取消订阅数据包的响应,来确认收到取消订阅请求。变量头包含数据包标识符和属性。Payload中包含退订原因码,其中每个原因码对应于退订请求中的相应主题筛选器。原因码为一个字节,用于指定成功、失败、与相对应的错误,例如“success”、“Not Authorized”、“topic filter Invalid”等等。规范中定义了7个退订原因代码。
0xD 下期预告
我们了解了MQTT的基本工作原理,那么从下期开始我们将从攻防的角度充分了解MQTT协议的安全性。下期的所有信息均基于我们在MQTT安全研究和物联网产品及基础设施渗透测试项目方面的经验。我将尝试解答一些在物联网渗透测试中遇到新协议时通常会出现的问题。敬请期待。
作者|李泉(liquan165) 高级安全研究员
主要研究领域|车联网、工控、物联网安全、二进制安全等
来源:freebuf.com 2021-07-13 13:40:52 by: 战争贩子
请登录后发表评论
注册