MQTT安全性设计详解

2019-12-23 15:39:05 浏览数 (1)

1 前言

MQTT(MQ Telemetry Transport, MQ 遥测传输)。它是一种发布/订阅、极其简单和轻量级的消息传递协议,旨在用于受限设备和低带宽,高延迟或不可靠的网络。设计原则是使网络带宽和设备资源要求最小化,同时还要尝试确保可靠性和一定程度的交付保证。这些原则也使该协议成为新兴的“M2M”或“物联网”连接设备世界的理想选择,并且适用于带宽和电池电量极为宝贵的移动应用。

物联网的前景变得越来越大,尤其是 5G 的到来,各领域如车联网、车载娱乐(AR 等)、智能电网、移动和协作机器人、智能视频监控、智慧城市等等。

5G 物联网用例图

IOT ANALYTICS机构预测到 2025 年全世界运行的物联网设备能达到 342 亿(34.2billion)。各互联网大头公司,如亚马逊、微软、阿里巴巴、腾讯、IBM 等等都推出了物联网云平台。而每个云平台都对 MQTT 协议支持,支持直接将设备通过 MQTT 协议与他们的云平台对接起来。MQTT 是一个开放的协议,我们可以自己去搭建自己的云平台,实现定制化开发,那么在实现 MQTT 安全上有什么需要注意的地方呢?本文从实现了 MQTT 协议的 mosquitto broker 具体示例来讲述。

IOT ANALYTICS物联网激活设备趋势预测图

2 安全实现方式

  • MQTT 协议本身支持用户名和密码实现客户端的身份校验
  • 使用 SSL(升级版本 TLS)对网络数据进行加密(这与 MQTT 协议本身是无关的,会增加网络开销)
  • 通过 Broker 配置对 Topic 的读写权限
  • 使用授权管理插件,实现批量级用户权限和 topic 的读写权限管理

2.1 使用用户名和密码限制连接

通过使用用户名和密码限制连接的方式,客户端连接 broker 时需要设置与 broker 要求的用户名密码才能够连接成功。

mosquitto.conf:

代码语言:javascript复制
# 指定用户名和密码才能连接broker
allow_anonymous false
password_file {your password path}/passwdfile

密码文件的生成:

代码语言:javascript复制
mosquitto_passwd [ -c | -D ] passwordfile username
mosquitto_passwd -b passwordfile username password
mosquitto_passwd -U passwordfile

-b
    以批处理模式运行。这允许在命令行提供密码,这可以很方便,但应小心使用,因为密码将在命令行和命令历史记录中可见
-c
    创建一个新的密码文件,如果文件已经存在,则会覆盖。输入命令后,控制台会提示输入新建用户的密码,连续输入两次密码后,则密码文件创建完成
-D
    从密码文件中删除指定的用户
-U
    此选项可用于使用哈希密码将带有纯文本密码的密码文件升级/转换为一个密码文件

2.2 使用 SSL(升级版本 TLS)对网络数据进行加密

使用 TLS 对网络数据加密,需要在配置文件中指定认证文件、密钥文件。

mosquitto.conf:

代码语言:javascript复制
cafile {your file path}/m2mqtt_ca.crt
certfile {your file path}/m2mqtt_srv.crt
keyfile {your file path}/m2mqtt_srv.key

如何签发证书,查看另一篇文章“使用 TLS 和 Mosquitto Broker 实现安全通信之密钥和证书生成”;

2.3 配置 Broker ACL

通过设置 Broker ACL,可以限制指定用户对指定 Topic 的数据读写权限。

mosquitto.conf:

代码语言:javascript复制
# 配置acl_file参数为指定acl文件
acl_file {your file path}/aclfile

aclfile 文件内容示例:

代码语言:javascript复制
# 如下配置会影响没有用户名的客户端的访问控制
# topic [read|write|readwrite] <topic>
topic read $SYS/#

# 如下配置会影响用户名为 "roger" 的访问控制.
user roger
topic foo/bar

# 如下配置会影响所有客户端
# pattern [read|write|readwrite] <topic>
pattern write $SYS/broker/connection/%c/state

2.4 使用授权管理插件,实现批量级用户权限和 topic 的读写权限管理

使用授权管理插件 https://github.com/jpmens/mosquitto-auth-plug

该插件可以执行身份验证(检查用户名/密码)和授权(通过 ACL 授予订阅和/或发布特定主题的许可),通过与数据库绑定,将身份验证和 ACL 管理录入数据库,从而很方便的实现用户身份验证管理。该插件对数据库 mysql 及 mongodb 支持很友好。具体怎么使用可以参考 README。这里仅给出 mysql 配置示例。

mosquitto.conf:

代码语言:javascript复制
# 指定插件所需动态库
auth_plugin /usr/mosquitto/bin/auth-plug.so
# 指定后端数据库
auth_opt_backends mysql
# 指定数据库主机地址
auth_opt_host localhost
# 指定数据库访问端口
auth_opt_port 3306
# 指定数据库名
auth_opt_dbname your_database
# 指定数据库访问用户名
auth_opt_user your_username
# 指定数据库访问密码
auth_opt_pass your_passwd
# 配置身份验证查询语句
auth_opt_userquery select (case password_syncstatus when 'sync' then user_passwd else old_password end) as user_passwd from tbl_mqttuser where user_name='%s'
# 配置ACL验证查询语句
auth_opt_aclquery SELECT topic FROM tbl_mqttacls WHERE (user_name = '%s') AND (rw >= %d)
# 匿名MQTT连接时插件配置的用户名
auth_opt_anonusername anonymouS

mysql 中 tbl_mqttuser 及 tbl_mqttacls 两个表数据示例:

代码语言:javascript复制
mysql> select * from  tbl_mqttacls;
 -------- -------------- -------- ---- 
| acl_id | user_name    | topic  | rw |
 -------- -------------- -------- ---- 
|      1 | user_1 | #      |  6 |
|      2 | user_2 | $SYS/# |  4 |
 -------- -------------- -------- ---- 
4 rows in set (0.00 sec)

mysql> select * from  tbl_mqttuser;
 --------- -------------- --------------------------------------------------------------------- --------------------------------------------------------------------- --------------------- --------------------- 
| user_id | user_name    | user_passwd                                                         | old_password

⚠️ 注意:对$SYS系统主题的权限设置需要注意读写权限,一般来说是不会开放写数据权限的,否则系统主题得到的数据就不是正确的统计数据了,可能是篡改掉了的。在生产环境中一般系统主题是不会开放的。

3 总结

  • 物联网的安全尤为重要,MQTT 作为广泛使用的轻量级协议,实现安全的方式有多种
  • MQTT 协议本身支持用户名和密码实现客户端的身份校验
  • 使用 SSL(升级版本 TLS)可以对网络数据进行加密(这与 MQTT 协议本身是无关的,会增加网络开销)
  • 通过 Broker 可以配置对 Topic 的读写权限
  • 使用授权管理插件,实现批量级用户权限和 topic 的读写权限管理
  • $SYS系统主题的权限设置需要注意读写权限,一般来说是不会开放写数据权限

4 扩展之 MQTT SYS 主题

MQTT v3.1.1 是较旧的 ISO 和 OASIS 标准,MQTT v5.0 是 OASIS 标准,该协议定义了静态主题、必须实现的SYS主题和非必须实现的主题。

静态 SYS 主题: 不需要在每个$SYS主题更新时间间隔上发送有关静态$SYS主题的消息,只有在订阅了之后才发送一次。**必选主题:**每个声称支持$SYS主题的代理(broker,如 mosquitto)都应支持这些主题,是每个 broker 都需要支持的。**可选主题:**代理可以选择性实现这些主题。

必选主题和可选主题中包含静态主题。

必选主题:

  • $SYS/broker/load/bytes/received : 自代理启动以来收到的字节总数。
  • $SYS/broker/load/bytes/sent : 自代理启动以来发送的字节总数。
  • $SYS/broker/clients/connected : 当前连接的客户端数
  • $SYS/broker/clients/disconnected : 在代理上注册但当前已断开连接的持久客户端总数(禁用了 clean session)。
  • $SYS/broker/clients/maximum : 已连接到代理的最大活动客户端数。仅在更新$SYS主题树时才计算此值,因此可能不计算短暂的客户端连接。
  • $SYS/broker/clients/total : 当前已在代理上连接并注册的持久会话的已连接和已断开连接的客户端总数。
  • $SYS/broker/messages/received : 自代理启动以来收到的任何类型的消息总数。
  • $SYS/broker/messages/sent : 自代理启动以来发送的任何类型的消息总数。
  • $SYS/broker/messages/publish/dropped : 由于运行中/排队限制而删除的发布消息总数。
  • $SYS/broker/messages/publish/received : 自代理启动以来收到的 PUBLISH 消息总数。
  • $SYS/broker/messages/publish/sent : 自代理启动以来发送的 PUBLISH 消息总数。
  • $SYS/broker/messages/retained/count: 代理上活动的保留消息总数。
  • $SYS/broker/subscriptions/count: 代理上活动的订阅总数。
  • $SYS/broker/uptime: 代理已联机的时间(以秒为单位)
  • $SYS/broker/version: broker 的版本。静态主题

可选主题:

  • $SYS/broker/time: 服务器上的当前时间
  • $SYS/broker/timestamp: 生成此特定版本的代理的时间戳。静态主题。
  • ......

5 参考链接

  • https://github.com/mqtt/mqtt.github.io/wiki/SYS-Topics
  • https://docs.vernemq.com/configuration/bridge
  • https://www.hivemq.com/blog/why-you-shouldnt-use-sys-topics-for-monitoring/
  • https://mosquitto.org/man/mosquitto-8.html
  • http://mqtt.org/faq
  • https://iot-analytics.com/state-of-the-iot-update-q1-q2-2018-number-of-iot-devices-now-7b/
  • https://blog.teserakt.io/2019/02/25/securing-the-mosquitto-mqtt-broker/
  • https://github.com/jpmens/mosquitto-auth-plug

0 人点赞