3. 消息签名与安全机制:签名原理、时间戳、密钥管理、防止重放攻击
聊到 MAVLink 的安全机制,我得先坦白一件事。早些年我做无人机巡检项目时,觉得通信安全是「大厂才需要考虑的事」。结果有一次在演示现场,隔壁团队用一台笔记本加一个 SDR 设备,就把我们无人机的位置、航点和传感器数据全抓出来了。虽然没造成实质损失,但那个画面让我印象很深——从那以后,我再也不敢轻视消息签名了。
MAVLink 2 引入的签名机制,说白了就是给每条消息加了一把「数字锁」。你想想看,无人机在天上飞,地面站发一条「紧急降落」指令,如果这条指令能被伪造或篡改,后果不堪设想。签名机制就是为了解决这个问题。
3.1 签名原理:消息的「身份证」
MAVLink 的签名基于 HMAC-SHA256。它不是对整个消息做加密,而是对消息的特定部分计算一个 6 字节的签名值,附加在消息末尾。
签名的计算范围包括:
- 消息头(包含长度、序列号、系统 ID、组件 ID、消息 ID)
- 消息载荷(即实际数据)
- 时间戳(8 字节,UNIX 微秒时间)
- 密钥 ID(1 字节,标识使用哪一组密钥)
计算过程大致是这样:
// 伪代码示意
signature = HMAC-SHA256(
key = secret_key[link_id],
data = header + payload + timestamp + secret_key[link_id]
)
// 取前 6 字节作为签名
final_signature = signature[0:6]
嗯,这里要注意:MAVLink 只取 HMAC 输出的前 6 字节。为什么是 6 字节?我个人的理解是,在安全性和带宽开销之间取了个平衡。6 字节意味着 2^48 种可能性,对于大多数无人机应用场景来说,暴力破解的代价已经足够高了。
核心要点:MAVLink 签名不是加密,而是认证。它保证消息的完整性和来源可信,但不保护消息内容的机密性。如果你需要加密载荷,得在应用层自己处理。
3.2 时间戳:防重放攻击的第一道防线
重放攻击是什么?简单说,就是攻击者截获一条合法消息,然后过一会儿再重新发送一遍。比如截获一条「解锁电机」的指令,等你降落之后再重放这条消息,无人机可能就会在空中突然解锁。
MAVLink 用时间戳来防止这种攻击。每条签名消息都带一个 8 字节的 UNIX 微秒时间戳。接收方会检查:
- 时间戳必须单调递增:新消息的时间戳必须大于之前收到的同源消息的时间戳。
- 时间戳不能偏差太大:与本地时间的差值不能超过预设的窗口(通常是 1 秒)。
我曾经在一个项目中遇到过时间戳同步的问题。无人机和地面站的时间差了 2 秒,结果所有签名消息都被丢弃了。排查了半天才发现是 NTP 服务没配置好。所以我的建议是:在部署前,一定要用 mavlink_sniffer 或类似工具先验证一下时间同步是否正常。
避坑指南:时间戳窗口不要设得太宽。我见过有人为了省事把窗口设成 10 秒,结果攻击者完全可以在 10 秒内完成重放攻击。1 秒是合理的默认值,如果你的网络延迟特别大,最多放宽到 2 秒。
3.3 密钥管理:安全的核心
签名算法再强,密钥泄露了也白搭。MAVLink 的密钥管理有几个关键点:
| 概念 | 说明 | 我的建议 |
|---|---|---|
| 密钥 ID | 1 字节,标识使用哪一组密钥 | 至少准备 2-3 组密钥,用于轮换 |
| 密钥长度 | 32 字节(256 位) | 不要用更短的密钥,HMAC-SHA256 需要 32 字节输入 |
| 密钥存储 | 在飞控和地面站中硬编码或通过安全通道分发 | 绝对不要明文写在配置文件里,至少做一次 XOR 混淆 |
| 密钥轮换 | 定期更换密钥,旧密钥保留一段时间的接收能力 | 我习惯每 30 天轮换一次,保留 7 天的旧密钥接收窗口 |
密钥分发的挑战在于:你需要在安全的环境下把密钥写入设备。对于量产无人机,我建议在产线烧录阶段就把密钥写入飞控的加密存储区。对于地面站,可以通过加密的 USB 密钥或二维码扫描的方式导入。
小技巧:如果你用的是 PX4 或 ArduPilot,它们都内置了签名支持。你只需要在参数中设置 MAV_SIGNING_KEY 和 MAV_SIGNING_MODE 即可。但注意,这些参数在固件编译后是可以通过地面站读取的——所以生产环境中一定要禁用参数读取功能。
3.4 防止重放攻击:不止是时间戳
时间戳能防大部分重放攻击,但有一个漏洞:如果攻击者在时间窗口内截获并立即重放,时间戳检查可能通过。所以 MAVLink 还引入了另一个机制——序列号。
每个发送端维护一个 16 位的序列号,随每条签名消息递增。接收端会记录每个发送端的最新序列号,只接受序列号更大的消息。这样即使攻击者在 1 秒内重放,序列号也会暴露它。
序列号和时间戳是配合使用的:
- 时间戳保证消息不会在长时间后被重放
- 序列号保证消息不会被立即重放
我记得有一次调试时发现,某个设备重启后序列号重置为 0,导致地面站拒绝接收它的消息。解决方案是在设备启动时,从非易失存储中读取上次的序列号,或者发送一条特殊的「重置序列号」消息。
3.5 签名机制的完整流程
说了这么多,我们来看一张完整的签名消息处理流程图:
从这张图可以看得很清楚:发送端和接收端的操作是对称的。接收端会用同样的密钥和同样的算法重新计算签名,然后比对。任何一个环节不匹配,消息就会被丢弃。
3.6 实际部署建议
最后,我总结几条在实际项目中验证过的经验:
- 先测试再上线:在开发环境中先用
MAV_SIGNING_MODE=1(仅接收签名消息)测试,确认所有设备都能正常通信后再切换到生产模式。 - 保留一个无签名通道:我习惯在调试端口上保留无签名模式,方便紧急情况下的诊断。但生产环境必须关闭。
- 密钥泄露怎么办:准备一个应急方案。比如通过遥控器的特定通道触发密钥更新,或者通过安全链路下发新密钥。
- 日志中不要记录密钥:这个错误我犯过。调试时把密钥打印到日志里,结果日志文件被上传到了公开的 bug 追踪系统……嗯,从那以后我写了个脚本自动扫描日志中的密钥模式。
一句话总结:MAVLink 签名机制的核心是「时间戳 + 序列号 + HMAC-SHA256」。时间戳防长时间重放,序列号防短时间重放,HMAC 保证消息完整性和来源可信。三者缺一不可。