1.研究背景和目的
1.1.研究背景
MQTT(Message Queuing Telemetry Transport,消息队列遥测传输)协议是物联网应用中重要的应用层协议,是IBM开发的一个即时通讯协议,它是一种轻量级的、基于代理的“发布/订阅”模式的消息传输协议,协议简洁、小巧、可扩展性强、省流量、省电等优点,而且已经有PHP,JAVA,Python,C,C#,Go等多个语言版本,基本可以使用在任何平台上,几乎可以把所有联网物品和外部连接起来。但如果MQTT协议不进行安全实现,黑客可以恶意发布信息给服务器,特别是在工业、交通等物联网应用场合,若遭受不法分子攻击,后果不堪设想。本文旨在构建MQTT协议的安全通信,以减小MQTT协议被攻击的风险。
1.2.研究目的
-
了解MQTT协议。 -
通过NB-IoT硬件实现MQTT协议的重放攻击和中间人攻击,并理解其原理。 -
通过Mosquitto平台理解MQTT协议的实现过程和原理。 -
掌握MQTT的安全通信的基本原理,设计MQTT安全通信的安全措施,实现基于TLS的MQTT通信。
2.MQTT
2.1.MQTT简介
MQTT(Message Queuing Telemetry Transport,消息队列遥测传输协议),是一种基于发布/订阅(publish/subscribe)模式的“轻量级”通讯协议,该协议构建于TCP/IP协议上,由IBM在1999年发布。MQTT最大优点在于,可以以极少的代码和有限的带宽,为连接远程设备提供实时可靠的消息服务。作为一种低开销、低带宽占用的即时通讯协议,使其在物联网、小型设备、移动应用等方面有较广泛的应用。由于MQTT协议轻量、简单、开放和易于实现的这些特点,它适用范围非常广泛。在很多情况下,包括受限的环境中,如:机器与机器(M2M)通信和物联网(IoT)。其次,通过卫星链路通信传感器、偶尔拨号的医疗设备、智能家居、及一些小型化设备中已广泛使用。
2.2.MQTT协议特点
-
使用的发布/订阅消息模式,提供了一对多消息分发。
-
对传输消息有三种服务质量(QoS):
最多一次,这一级别会发生消息丢失或重复,消息发布依赖于底层TCP/IP网络即:<=1
至多一次,这一级别会确保消息到达,但消息可能会重复。即:>=1
只有一次,确保消息只有一次到达。即:=1。在一些要求比较严格的计费系统中,可以使用此级别
-
通知机制,异常中断时通知传输双方。
2.3.MQTT协议实现
-
实现MQTT协议需要:客户端和服务器端
-
MQTT协议中有三种身份:发布者(Publish)、代理(Broker)(服务器)、订阅者(Subscribe)。其中,消息的发布者和订阅者都是客户端,消息代理是服务器,消息发布者可以同时是订阅者。
-
MQTT传输的消息分为:主题(Topic)和负载(payload)两部分
(1)Topic,可以理解为消息的类型,订阅者订阅(Subscribe)后,就会收到该主题的消息内容(payload)
(2)payload,可以理解为消息的内容,是指订阅者具体要使用的内容
2.4.MQTT客户端
一个使用MQTT协议的应用程序或者设备,它总是建立到服务器的网络连接。
客户端可以:
-
发布其他客户端可能会订阅的信息
-
订阅其它客户端发布的消息
-
退订或删除应用程序的消息
-
断开与服务器连接
2.5.MQTT服务器
MQTT服务器被称为“消息代理”(Broker),可以是一个应用程序或一台设备。它是位于消息发布者和订阅者之间。
代理服务器可以:
-
接受来自客户的网络连接
-
接受客户发布的应用信息
-
处理来自客户端的订阅和退订请求
-
向订阅的客户转发应用程序消息
2.6.MQTT协议的订阅、主题、会话
-
订阅(Subscription)
订阅包含主题筛选器(Topic Filter)和最大服务质量(QoS)。订阅会与一个会话(Session)关联。一个会话可以包含多个订阅。每一个会话中的每个订阅都有一个不同的主题筛选器。
-
会话(Session)
每个客户端与服务器建立连接后就是一个会话,客户端和服务器之间有状态交互。会话存在于一个网络之间,也可能在客户端和服务器之间跨越多个连续的网络连接。
-
主题名(Topic Name)
连接到一个应用程序消息的标签,该标签与服务器的订阅相匹配。服务器会将消息发送给订阅所匹配标签的每个客户端。
-
主题筛选器(Topic Filter)
一个对主题名通配符筛选器,在订阅表达式中使用,表示订阅所匹配到的多个主题。
-
负载(Payload)
消息订阅者所具体接收的内容
2.7.MQTT协议中的方法
MQTT协议中定义了一些方法(也被称为动作), 表示对确定资源进行操作。这个资源可以代表预先存在的数据或动态生成数据,这取决于服务器的实现。通常来说,资源指服务器上的文件或输出。
-
Connect
等待与服务器建立连接。
-
Disconnect
等待MQTT客户端完成所做的工作,并与服务器断开TCP/IP会话。
-
Subscribe
等待完成订阅。
-
UnSubscribe
等待服务器取消客户端的一个或多个topics订阅。
-
Publish
MQTT客户端发送消息请求,发送完成后返回应用程序线程。
3.基于NB-IoT的MQTT简单通信实验
3.1.NB-IoT简介
物联网(The Internet of things,IoT)顾名思义,就是物与物相连的互联网。这有两层意思:第一,物联网的核心和基础仍然是互联网,是在互联网基础上的延伸和扩展的网络;第二,其用户端延伸和扩展到了任何物品与物品之间,进行信息交换和通信。
窄带物联网(Narrow Band Internet of Things,NB-IoT)是物联网领域一个新兴的技术,主要用于低移动性、小数据量、对时延不敏感的连接服务,其支持低功耗设备在网络中的数据传输,因此也是一种低功耗广域网(Low Power Wide Area Network,LPWAN)通信技术。相对于被逐渐淘汰的2G通信,NB-IoT具有三大优势:
大连接:海量链接的能力,在同一基站的情况下, NB-IoT可以比现有无线技术提供 50~100 倍的接入数。一个扇区能够支持,10 万个连接,设备成本与功耗有效降低,网络架构得到优化。
广覆盖:在同样的频段下, NB-IoT比现有的网络增益提升了 20 dB,相当于提升了 100 倍的覆盖面积。
低功耗:NB-IoT借助 PSM(Power Saving Mode,节电模式)和 eDRX(Extended Discontinuous Reception,超长非连续接收)可实现更长待机。
这是一张典型的MQTT在NB-IoT中的应用,其中app作为客户端,智能网关也作为客户端,物联网云服务作为统一的服务端负责收发,app和V6云订阅同一个主题,app和网关订阅另一个主题,当每次需要发送mqtt命令时首先判断是账号登录还是网关登录, publish对应topic和payload,物联网云服务收到mqtt消息后转发给订阅同一个topic主题的客户端,然后通过再由网关发送对应的指令去控制低功耗设备(比如门锁的开关,灯泡的开关)。这个时候可以看出topic的重要性,合理定义topic可以实现不同业务功能。
3.2.测试环境和实验工具**
-
1.测试环境
LAPTOP-V0O3D3O6 OS版本 windows10
IP:192.168.43.163 MAC: IntelCor_5d:dd:60 (28:d0:ea:5d:dd:60)
-
2.实验工具
硬件:
DFR0530 TinkerNode NB-IoT 开发板、
PCB天线
NB-IoT物联网专用SIM卡
USB数据线
软件:
Arduino IDE(Integrated Development Environment)
相当于编辑器编译器加连接器+其他。Arduino IDE就是Arduino团队提供的一款专门为Arduino设计的编程软件,使用它,我们便能将程序从代码上传至Arduino主板。
Arduino IDE要求:
Arduino采用串口下载代码 存储到内部的 flash 中,也就是我们将程序烧录到Arduino中。
Burp Suite 是用于攻击web 应用程序的集成平台,包含了许多工具。Burp Suite为这些工具设计了许多接口,以加快攻击应用程序的过程。所有工具都共享一个请求,并能处理对应的HTTP 消息、持久性、认证、代理、日志、警报。
使用前要配置好相关代理。
阿里云平台作为设备云端控制器创建项目,实现指令的发布。
3.3.实验内容
-
1.硬件设备调试
A.选择好对应的例程
B.添加开发板管理器网址
C.加载板卡驱动
D.配置Arduino开发板的下载口
E.选择Arduino开发板型号
F.烧录代码至设备
/*!
* @file Bedroom_Light.ino
*
* @brief Simulate esp32 as a bedroom light and use Aliyun as a cloud platform.
* @n Subscribe to the switch status theme to enable remote switch control bedroom lights
*
* @copyright Copyright (c) 2010 DFRobot Co.Ltd (http://www.dfrobot.com)
* @licence The MIT License (MIT)
* @author [Wuxiao]([email protected])
* @version V1.0
* @date 2019-02-10
* @get from https://www.dfrobot.com
*/
#include <WiFi.h>
#include <PubSubClient.h>
#include <ArduinoJson.h>
#include "DFRobot_Iot.h"
#define BEDROOD_LIGHT D4
/*Set WIFI name and password*/
const char *WIFI_SSID = "sfe";
const char *WIFI_PASSWORD = "00000000";
/*Configure device certificate information*/
String ProductKey = "glp8FjnaCtT";
String ClientId = "12345"; /*Custom id*/
String DeviceName = "3uglAEsRkm0UniVJU9vT";
String DeviceSecret = "2fa79078d09ac4a7e9e50a9e4983206b";
/*Configure the domain name and port number*/
String ALIYUN_SERVER = "iot-as-mqtt.cn-shanghai.aliyuncs.com";
uint16_t PORT = 1883;
/*Product identifier that needs to be operated*/
String Identifier = "LightStatus";
/*TOPIC that need to be published and subscribed*/
const char *subTopic = "/glp8FjnaCtT/3uglAEsRkm0UniVJU9vT/user/update"; //****set
const char *pubTopic = "/glp8FjnaCtT/3uglAEsRkm0UniVJU9vT/user/get"; //******post
DFRobot_Iot myIot;
WiFiClient espClient;
PubSubClient client(espClient);
static void openLight()
{
digitalWrite(BEDROOD_LIGHT, HIGH);
}
static void closeLight()
{
digitalWrite(BEDROOD_LIGHT, LOW);
}
void connectWiFi()
{
Serial.print("Connecting to ");
Serial.println(WIFI_SSID);
WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
while (WiFi.status() != WL_CONNECTED)
{
delay(500);
Serial.print(".");
}
Serial.println();
Serial.println("WiFi connected");
Serial.print("IP Adderss: ");
Serial.println(WiFi.localIP());
}
void callback(char *topic, byte *payload, unsigned int len)
{
Serial.print("Recevice [");
Serial.print(topic);
Serial.print("] ");
for (int i = 0; i < len; i++)
{
Serial.print((char)payload[i]);
}
Serial.println();
StaticJsonDocument<300> jsonBuffer;
deserializeJson(jsonBuffer, (const char *)payload);
auto error = deserializeJson(jsonBuffer, (const char *)payload);
if (error)
{
Serial.println("parseObject() failed");
return;
}
const uint16_t LightStatus = jsonBuffer["params"][Identifier];
if (LightStatus == 1)
{
openLight();
}
else
{
closeLight();
}
String tempMseg = "{"id":" + ClientId + ","params":{"" + Identifier + "":" + (String)LightStatus + "},"method":"thing.event.property.post"}";
char sendMseg[tempMseg.length()];
strcpy(sendMseg, tempMseg.c_str());
client.publish(pubTopic, sendMseg);
}
void ConnectCloud()
{
while (!client.connected())
{
Serial.print("Attempting MQTT connection...");
/*A device connected to the cloud platform based on an automatically calculated username and password*/
if (client.connect(myIot._clientId, myIot._username, myIot._password))
{
Serial.println("connected");
client.subscribe(subTopic);
}
else
{
Serial.print("failed, rc=");
Serial.print(client.state());
Serial.println(" try again in 5 seconds");
delay(5000);
}
}
}
void setup()
{
Serial.begin(115200);
pinMode(BEDROOD_LIGHT, OUTPUT);
/*Connect to WIFI*/
connectWiFi();
/*Initialize the configuration of Aliyun*/
myIot.init(ALIYUN_SERVER, ProductKey, ClientId, DeviceName, DeviceSecret);
client.setServer(myIot._mqttServer, PORT);
/*Set the callback function to execute the callback function when receiving the subscription information*/
client.setCallback(callback);
/*Connect to the cloud platform*/
ConnectCloud();
/*Turn off the lights first*/
closeLight();
/*Publish information about turning off the lights*/
client.publish(pubTopic, ("{"id":" + ClientId + ","params":{"" + Identifier + "":0},"method":"thing.event.property.post"}").c_str());
}
void loop()
{
if (!client.connected())
{
ConnectCloud();
}
client.loop();
}
-
阿里云平台设置
A.注册账号
B.创建产品
C.添加功能
D.添加设备
E.与产品连接,使产品处于在线状态
(3)下发开灯指令
(4)下发关灯指令
(5)指令抓包
可以看到报文以明文形式传输。
(6)重放攻击
采用BurpSuite进行消息拦截,保存,并重发。
Step1:发布开灯指令
Step2:发布关灯指令
Step3:新建指令,将第一次的开灯指令重放
(7)中间人攻击
采用BurpSuite进行消息拦截,修改,并重发。
Step1:开灯指令
Step2:将开灯指令整个替换成关灯指令发送给设备并收到响应
4.基于Mosquitto的安全通信实验
4.1.Mosquitto简介
Mosquitto是一款实现了消息推送协议 MQTT v3.1 的开源消息代理软件,提供轻量级的,支持可发布/可订阅的的消息推送模式,使设备对设备之间的短消息通信变得简单,比如现在应用广泛的低功耗传感器,手机、嵌入式计算机、微型控制器等移动设备。
4.2.测试环境和实验工具
-
1.测试环境
Windows:
IP: 10.211.55.16 MAC: 00-1C-42-53-6D-59
Kali Linux:
Linux orz 5.14.0-kali4-amd64 #1 SMP Debian 5.14.16-1kali1 (2021-11-05) x86_64 GNU/Linux
IP: 192.168.53.131 MAC: 00:0c:29:13:6b:6e
Mosquitto:
MQTT version 5.0/3.1.1/3.1 client library
MQTT version 5.0/3.1.1/3.1 compatible message broker
-
2.实验工具
-
VMware Workstation
是一款功能强大的桌面虚拟计算机软件,提供用户可在单一的桌面上同时运行不同的操作系统,和进行开发、测试 、部署新的应用程序的最佳解决方案。VMware Workstation可在一部实体机器上模拟完整的网络环境,以及可便于携带的虚拟机器,其更好的灵活性与先进的技术胜过了市面上其他的虚拟计算机软件。
-
Kali Linux
是基于Debian的Linux发行版, 设计用于数字取证操作系统Kali Linux预装了许多渗透测试软件,包括nmap 、Wireshark 、John the Ripper,以及Aircrack-ng. 用户可通过硬盘、live CD或live USB运行Kali Linux。Kali Linux既有32位和64位的镜像。可用于x86 指令集。
-
FinalShell
是一款免费的国产的集 SSH 工具、服务器管理、远程桌面加速的良心软件,同时支持 Windows, macOS, Linux,它不单单是一个 SSH 工具,完整的说法应该叫一体化的服务器,网络管理软件,在很大程度上可以免费替代 XShell,是国产中不多见的良心产品,具有免费海外服务器远程桌面加速,ssh 加速,双边 tcp 加速,内网穿透等特色功能。
-
MQTT.fx
是一款基于Eclipse Paho,使用Java语言编写的MQTT客户端工具。支持通过Topic订阅和发布消息,用来前期和物理云平台调试非常方便。
4.3.实验内容
1.使用FinalShell连接kali虚拟机完成Mosquitto环境搭建。搭建环境完成后使用MQTT.fx调试(MQTT.fx在使用之前要配置好相关参数确保与kali完成连接),订阅和发布消息,同时抓包。
抓包得到的报文与在实际场景下的报文均为明文传输。
2.在FinalShell上, 配置Mosquitto的TLS双向认证
Step1 生成证书文件,包括产生CA的key和证书文件,生成server和client端证书。
Step2 修改mosquitto配置文件,之后重启mosquitto。
Step3 双向认证测试,首先生成mosquitto连接用户名和密码,再执行测试命令。
3.抓包验证
4.4.实验分析
客户端与服务端通过tcp三次握手建立tcp连接后,客户端首先向服务器发出建立加密通信的请求,发送ClientHello请求,从消息体结构看,tls/ssl是基于tcp连接之上,应用层之下的协议。
服务器收到客户端请求后,向客户端发出响应,叫做Sever Hello。
从消息体中,可以看到服务器的响应包含以下内容:
(1) 确认使用的加密通信协议版本,这里确认使用tls1.2,而不是client hello中的tls1.1。响应握手协议消息 server hello。如果浏览器与服务器支持的版本不一致,服务器关闭加密通信。
(2) 一个服务器生成的随机数,稍后用于生成”对话密钥”。
(3) 确认使用的加密套件,这里为rsa+aes128+sha256
客户端接收到server hello握手消息后,及时反馈ack消息。服务端接收客户端ack消息后,发送服务端电子证书,密钥交换,及server hello done三个握手消息。
从封装内容看,包含两层ssl协议体信息,头一个为服务端证书,后面跟着公共密钥交换和hello done消息体,具体如下:
(1)详细的电子证书信息和CA认证机构信息
(2)密钥交换信息,包括DH算法计算出的pubkey公钥,电子签名的hash算法值
(3)server hello done消息体
客户端发送ack消息给服务端,确认收到server hello done消息,然后发送客户端的密钥交换信息和修改密钥的协议消息
主要内容如下:
(1) 发送DH算法计算的pubkey,用于服务端计算生成解密私钥
(2) 发送编码改变通知,表示随后的信息都将用双方商定的加密方法和密钥发送。
(3) 发送加密后的握手消息,一个随机数。该随机数用服务器公钥加密,防止被窃听
(4) 客户端握手结束通知,表示客户端的握手阶段已经结束。这一项同时也是前面发送的所有内容的hash值,用来供服务器校验。(可能在加密消息中,未确认)
客户端收到服务器所有响应消息后,首先验证服务器证书。如果证书不是可信机构颁布、或者证书中的域名与实际域名不一致、或者证书已经过期,就会向访问者显示一个警告,由其选择是否还要继续通信。
如果证书没有问题,客户端就会从证书中取出服务器的公钥,即server key exchange消息中携带的pubkey值。然后,根据已经收到的三个随机数计算书加密密钥,对握手信息进行加密通信,然后向服务器发送上面抓包中三项信息内容。
至此,整个握手阶段全部结束。接下来,客户端与服务器进入加密通信,就完全是使用普通的HTTP协议,只不过用”会话密钥”加密数据内容。
5.总结展望
通过在实际场景下的研究发现,MQTT协议架构在TCP协议之上的传输过程中是以明文形式传输的,这使得传输设备指纹特征,存在身份复用的风险,以及信息被拦截修改重定向。考虑到协议的安全性,为了保证设备信息完整性,服务器收到准确完整的信息、减小设备身份复用风险、防止遭遇拒绝服务攻击等,加入了协议的TLS双向认证,将原有明文实现加密。实验证明这种方法可以有效避免上述威胁。然而TLS会减小连接实效性,对低运算能力的设备而言是额外的负担,若设备是长连接可以避免被反复连接的。引入TLS机制固然是能够保证安全,但却在TCP握手和HTTP通信之间,多加了两个往返的TLS握手过程,可能会遭到中间人攻击。
随着网络安全技术的发展,网络研究人员又提出了很多安全方案。在基础设施方面主要是通过常见的防火墙等手段,保证接入的数据是安全的。在操作系统层面加入系统相关的安全技术。重点是在传输层和应用层上的安全防护,传输层基于TLS/SSL,通过使用安全端口,证书和有效载荷加密等方式保证数据传输中的安全,应用层则重点是基于客户端ID/ACL和RBAC的方式授权客户端在broker上操作的范围,比如可以访问哪些主题,这些主题是可以订阅还是发布,可以使用的QOS等级等;除此以外在实际的使用场景中,还会根据具体的业务情况,通过节流,限制消息大小、数据加密等方式防止外部攻击。如下图所示是一种非常典型的在应用层对客户端,可以对访问的主题范围进行认证和授权。通过这种类似于操作系统中的用户组的方式,保障了数据的安全访问。MQTT协议在安全性上有了很大的保障。相信将来MQTT协议会得到更好的应用。
6.参考文献
-
TCP/IP详解,卷1:协议/(美)W.Richhard Stevwns 著;范建华等译;–北京:机械工业出版社,2000.4 -
窄带物联网(NB-IoT)标准与关键技术;戴博,袁弋非,余媛芳;人民邮电出版社;2016 -
万物互联NB-IoT关键技术与应用实践;郭宝,张阳,顾安,刘毅;机械工业出版社;2017 -
基于MQTT协议的Mosquitto数据订阅机制优化与性能分析;黄迪,徐湛, 田志刚,如昕;物联网技术;2021 -
消息队列遥测传输协议(MQTT)技术分析;严彦欢;电子技术与软件工程;2021
原文始发于微信公众号(攻防SRC):MQTT协议的安全性研究:历史背景、发展历程及实际应用分析