ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

从串口到TCP/IP:嵌入式通信安全加固实战指南

从串口到TCP/IP:嵌入式通信安全加固实战指南 干通信的人尤其是做嵌入式或者网络协议栈的只要项目一跑起来早晚会遇到这种场景设备在实验室里一切正常一到现场就出幺蛾子不是串口乱码就是总线偶发报错再要么就是明明两台电脑都在同一局域网UDP 消息却像丢了魂一样收不到。等你把波特率、校验位、IP 地址、防火墙全部查一遍最后才发现问题不在“通不通”而在“安不安全”——有人在中间动了手脚或者设备本身对任何输入都照单全收。我早年在嵌入式行业做固件开发后来转网络安全做了几年安全评估和攻防演练最大的感受是很多系统被攻破不是加密算法不够强也不是服务器配置不到位而是通信链路这个“毛细血管”被严重忽略了。无论是板子上一根串口线、一条 I2C 总线还是设备之间一段 UDP 报文只要有人能碰到这条链路数据就不再有秘密。这篇文章我想把这些年踩过的坑、补过的洞、总结出来的方法一次性讲清楚内容覆盖硬件接口通信UART、I2C、SPI、CAN、485、网络通信TCP/UDP、组件通信与 IPC以及通信安全加固的具体套路。适合嵌入式工程师、上位机开发、物联网从业者也适合准备进入网络安全方向、想理解协议层安全问题的朋友。1. 为什么通信环节最容易成为安全短板1.1 通信协议的第一性问题先解决“通”再考虑“安全”几乎所有的通信协议不管是硬件层面的 UART、I2C、SPI、CAN还是网络层面的 TCP/IP、UDP设计之初的第一目标永远是“可靠地把数据从 A 送到 B”而不是“防着别人偷看和篡改”。这不是设计者的失误而是历史原因决定的。早年这些协议跑在封闭的物理环境里比如一块电路板上、一台设备内部、一个工厂车间里接触链路的人都是可信的自然不需要把安全放在第一位。但现在的设备早就不是孤岛了。一块 STM32 开发板的串口可能接着 Wi-Fi 模组Wi-Fi 模组又连着云端平台一台老旧的 PLC 通过 485 总线接了好几个从站而这些从站又可能通过网关连到了办公网。当“可信物理环境”这个假设被打破协议层不设防的问题就全暴露出来了。我经常用一个递纸条的类比来解释这个问题两个人传纸条如果整间教室只有他们两个纸条上写什么都无所谓但如果走廊里站着其他人那这张纸条就必须装进信封、写上签名、封好封口、编号登记否则被人看了、换了、撕了都毫无察觉。通信安全要做的事就是给原本“裸奔”的纸条加上信封、签名、封条和编号。1.2 攻击者为什么喜欢盯通信链路做过安全测试的人都清楚攻击者最喜欢找的就是“不需要复杂前置条件就能拿到敏感数据”的点通信链路恰好完美满足这个条件。首先是暴露面大。一个系统无论软件做得多安全数据总要跨越物理介质从总线到接口从网线到无线信道每跨一段就多一个可以被监听或插手的点。其次是通信协议往往缺少认证机制攻击者不需要知道任何口令就能往总线上发报文或者伪装成合法设备。最后是通信链路的“隐身性”——很多开发团队花大量精力保护 Web 页面、数据库和服务器操作系统却忘了抓包看一眼自己的设备在通信过程中传输的指令竟是完全明文。在真实的攻防演练里我见过太多案例设备固件里写着硬编码密钥串口调试接口没有关闭CAN 总线对伪造报文来者不拒Modbus 协议连最基本的设备认证都没有。这些问题用高深的黑客技术吗完全不用一根 USB 转串口线、一个 CAN 分析仪、一台装了 Wireshark 的笔记本就能做到。说句实在话通信安全做得好不好不取决于攻击者水平高不高而取决于你的系统有没有把“链路不可信”当成默认前提。2. 从串口到总线嵌入式通信协议的安全盲区2.1 串口通信UART暴露的调试后门UART 串口可能是嵌入式开发里最常用、也最容易被忽视安全的通信方式。STM32F103C8T6 的调试串口、ESP8266 的日志输出、路由器主板的 console 口本质上都是 UART。它只有 TX、RX 两根数据线加上地线没有时钟线通信双方约定好波特率就能收发简单到不能再简单。但正因为简单它几乎没有安全属性。没有设备地址没有密码校验没有加密机制连数据格式都靠双方事先约定。攻击者只要想办法把调试串口的物理引脚引出来用 USB 转 TTL 模块接上就能在终端里看到设备的所有日志输出。更麻烦的是很多嵌入式设备把引导加载程序Bootloader的交互入口也放在了 UART 上攻击者可以在开机时打断启动流程进入固件烧录模式直接 dump 整个 Flash再逆向出固件里的业务逻辑和密钥。我评估过的设备里至少有三分之一存在“生产环境未禁用调试串口”的问题。很多人觉得“外壳都封死了别人碰不到里面的引脚”但实际场景里调试触点往往以排针或测试焊盘的形式留在 PCB 上拆卸外壳、飞线读取并不难。串口安全的最低要求有三条量产版本关闭调试输出、Bootloader 必须加访问认证、如果确实要保留调试口至少把它放在只有拆机才能碰到的地方并配合登录口令使用。2.2 I2C 与 SPI板级通信的“家贼难防”I2C 和 SPI 是典型的板级通信协议一个用两根线SCL、SDA挂多个设备一个用主从选择线配合时钟和数据线高速传输。这两种协议在设计时默认“总线上的设备都是自己人”所以没有任何认证和加密。传感器数据、EEPROM 内容、ADC 采样值、配置寄存器全都明文在总线上跑。问题在于板级总线并不意味着绝对安全。首先很多芯片的调试接口比如 ARM Cortex-M 的 SWD、JTAG和板上 I2C/SPI 设备挂在一起攻击者通过调试接口可以直接操作 CPU绕过软件层的所有安全逻辑。其次如果某个板上传感器或存储芯片可以被物理替换攻击者完全可以伪造一个“假传感器”挂到 I2C 总线上让主控读到精心构造的数据。我在一次工控设备评估中就用过一个很原始的方法把设备外壳打开用夹具夹住 SPI Flash 的引脚在系统启动前把配置数据改了设备就依照伪造参数运行了。对于板级通信我的建议是把“物理接触完全控制”这条假设写进威胁模型。如果设备面临拆机攻击的风险那就要考虑使用带安全启动的 MCU、对敏感存储区域做加密、在关键传感器数据上附加 MAC 校验否则单纯依赖 I2C/SPI 的“正常读写流程”是挡不住有物理访问权限的攻击者的。2.3 CAN 总线与 485 通信工业现场的安全短板CAN 总线在汽车和工业控制中太常见了。它以短帧、广播、多主方式工作报文里有仲裁 ID 但没有源地址和目的地址也就是说任何节点都能往总线上发消息所有节点都会收到。这种设计非常适合实时控制但对安全来说就是“灾难级”的默认配置。攻击者只要把一个节点接入 CAN 总线或者攻破一个联网的网关就能发送伪造的转速、车速、刹车等控制报文。业内为了保护汽车 CAN 网络提出了 SecOCSecure Onboard Communication方案核心是给报文附加认证信息和新鲜度值防止伪造和重放但推了很多年存量设备依然大量裸奔。485 通信RS-485和基于它构建的 Modbus 协议在工业现场还有大量部署。Modbus RTU 使用功能码读写寄存器没有设备认证没有报文加密任何能接触到总线的主站都能控制所有从站。而且很多 485 链路是手拉手串接的只要中间有一段线缆暴露在外面攻击者并接两根线就能监听指令、篡改数据。在工控安全评估中Modbus 协议被利用的频率非常高因为它太“透明”了。如果正在做 CAN 或 485 项目的选型我的建议是至少在应用层增加一层自己的安全包装。CAN 报文 8 字节虽然紧张但也能挤出几个比特做消息认证Modbus 则可以在保持协议兼容的前提下把寄存器里的关键数据做加密混淆并在主站和从站之间增加挑战应答式认证防止指令被离线构造。3. 网络通信和组件通信边界在哪风险就在哪3.1 TCP/IP 明文传输与中间人风险从嵌入式设备到服务器从手机 App 到后台接口只要走网络就绕不开 TCP/IP 协议族。但 TCP/IP 本身从设计上也没有安全机制它只负责可靠传输和路由不负责加密、不负责认证、不负责告诉你对端是不是真的对端。HTTP、FTP、Telnet、Modbus TCP这些“高龄”协议里跑的数据基本都是明文攻击者在链路上抓包就能直接还原出账密、控制指令、业务数据。更危险的是中间人攻击。攻击者如果能把网络流量引到自己的机器上就可以在客户端和服务器之间“代理”全部通信向客户端冒充服务器向服务器冒充客户端。TCP 三次握手、序列号预测、ARP 欺骗、DNS 劫持这些都是实现中间人攻击的经典手法。很多开发者觉得“我在内网部署的不会有中间人”但内网里恶意员工、被攻陷的打印机、不安全的 Wi-Fi 接入点都是现成的中间人跳板。防护的手段大家也都知道全链路走 TLS禁用 SSLv3 和 TLS 1.0 这类老旧版本校验服务器证书最好再做客户端证书双向认证。真正的问题从来不是“不知道用什么”而是“觉得没必要用”。我见过很多设备为了省 MCU 资源把 MQTT 甚至 HTTP 裸奔到公网结果固件升级包、控制指令全部明文传输被改一个字节设备就“叛变”了。3.2 UDP 通信低开销背后的伪包风险UDP 无连接、无状态、不重传很多实时场景喜欢用它比如音视频传输、游戏同步、设备发现。但低开销的代价是攻击者可以轻易伪造 UDP 报文而且不需要像 TCP 那样先完成握手。只要知道目标 IP 和端口就可以伪造任意的源地址向目标发送数据包。两台电脑用网络调试助手做 UDP 通信测试时看着挺正常但稍微了解一下抓包数据就会发现UDP 报文里连“这条消息是谁发的”都说不清楚。UDP 通信的安全设计必须放在应用层自己实现。常见做法是每个报文加单调递增的序列号对付重放用共享密钥对报文内容做 HMAC对付篡改和伪造对等方之间先通过带外方式交换公钥或预置凭据解决身份认证如果是公网通信优先使用基于 UDP 的加密传输协议比如 QUIC或者在 UDP 之上自己封装一层加密隧道。用网络调试助手做 UDP 联调时我建议顺手把 Wireshark 挂着看一下有没有其他设备在往这个端口发数据。很多时候不是应用回包慢而是局域网内存在广播风暴或者别的设备在捣乱这类问题靠“看一眼收到的数据内容”往往发现不了必须结合抓包才能定位。3.3 组件通信与 IPC看不见的内部边界通信安全不只存在于设备和设备之间也在一个进程内部或一个应用系统内部。前端开发里的组件通信父传子、子传父、事件总线后端开发里的进程间通信IPC如管道、消息队列、共享内存、Unix Domain Socket微服务架构里的 RPC 调用本质上都是数据在“不同的信任域”之间流动。前端组件通信最常见的问题是信任边界混乱。父组件向子组件传一个对象子组件不加校验就直接渲染如果这个数据包含用户可控内容很容易变成 XSS 注入点。子传父的情况也一样子组件向父组件 emit 一个事件如果父组件没有做数据校验就把它当成可信参数使用等于自己给自己开了后门。我写过不少前端代码也审过不少前端项目最深的体会是组件之间的传值一定要“入境检查”不能因为是“自己家组件”就放松警惕。进程间通信和微服务调用则是另一种典型问题。很多系统的多个微服务之间使用内部 API 通信但没有做认证和鉴权一旦其中一个服务被攻破攻击者就能通过内部接口横向移动把所有服务的数据都拖走。正确的做法是内部接口至少要用 mTLS 或服务网格做双向身份认证消息队列要按 topic 配置 ACL共享内存和消息队列里的数据进程退出后要及时清空防止敏感信息残留在内存中被其他进程读取。边界信任的崩溃往往不是从外部开始而是从内部先失守的。4. 通信加固的核心思路认证、加密、完整性、防重放4.1 身份认证确认对端是谁通信双方在交换数据之前首先要解决“你是谁”的问题。身份认证的方法很多从最简单的预共享密钥PSK到证书体系再到挑战应答协议安全强度依次递增开销也依次递增。选择哪种认证方式取决于通信节点的计算能力、带宽成本和威胁等级。嵌入式设备里如果走串口或 CAN 总线常见方案是预共享密钥加挑战应答A 端向 B 端发送一个随机数 challengeB 端用共享密钥对 challenge 做哈希或加密把结果返回A 端验算一致才继续。这个流程看上去简单但能挡住大多数“直接发指令”的伪造攻击因为它让攻击者无法离线构造合法报文。如果是网络通信强烈建议使用 TLS 证书而不是简单的账号密码。TLS 双向认证要求服务器有证书客户端也有证书两边在握手阶段互相验证比单向认证更不容易被中间人“指鹿为马”。公网场景可以借助权威 CA内网场景可以自建 CA只要把根证书分发到所有设备上就行。4.2 加密传输让数据不可读认证解决的是“对方是谁”加密解决的是“数据被别人看到了也不怕”。加密算法的选择没有想象中复杂通信数据量小、性能受限的可以用 AES-GCM它同时提供加密和完整性校验网络传输则直接交给 TLS 或 QUIC不要自己实现加密协议。我见过不少团队想“自己发明一个加密算法”用了各种位运算、置换表、自定义加盐方式结果在安全评估中几分钟就被逆出来了。这里说一个原则永远使用业界广泛审核过的标准算法把精力花在密钥管理和使用方式上而不是把时间浪费在发明新算法上。AES-128 在当前安全强度下完全够用真正出问题的往往是密钥存放方式硬编码在固件里、写在配置文件的明文里、或者几个设备共用一个密钥。密钥一旦泄露算法再强也等于零。4.3 完整性校验与防重放防止篡改和重演加密只能防止数据被读不能防止数据被改。数据在传输过程中可能被截断后篡改哪怕攻击者看不懂明文也能在密文上“盲改”出错误数据。因此完整性校验必须单独做。常见的做法是 HMAC基于哈希的消息认证码发送方用共享密钥对报文内容计算 HMAC接收方用同样方式计算并对比不一致就丢弃报文。注意CRC 循环冗余校验只能发现随机噪声导致的误码它没有密钥攻击者可以在篡改数据后同步重算 CRC所以 CRC 绝对不能替代 HMAC。防重放和完整性校验经常一起做。重放攻击的原理是攻击者不需要理解报文内容只要把某个合法报文原样再发一遍就可能触发一次非法指令。比如“开门”指令被录下来攻击者以后反复发送这条指令就能随时开门。防重放的手段包括报文携带单调递增的序列号、时间戳、一次性随机数接收方记录最近已处理过的序列号或随机数发现重复就丢弃。对 TLS 来说协议内部天然有序列号机制不需要额外处理但对裸 TCP、UDP、CAN、Modbus就必须在应用层补齐。这里分享一个我自己总结的口诀通信数据的安全防护就像寄快递——加密是给快递上锁认证是验证收件人身份完整性校验是检查包裹有没有被拆过防重放则是给每一个包裹打上唯一运单号。四者配合才能真正保证通信链路可信。5. 一套可落地的通信安全加固流程5.1 梳理通信链路清单很多人做通信安全觉得很抽象不知道从哪入手。我的建议是第一步先不碰任何工具拿一张纸把所有通信链路画出来板子上的 UART 接了谁I2C 总线上挂了哪些设备CAN 总线有几个节点哪些设备走了以太网哪些使用了无线模块组件之间有哪些数据交互微服务之间有哪些调用。把每一条链路都列出来然后逐条回答三个问题这条链路上的数据是敏感数据吗如果被窃听、篡改、重放会造成什么后果当前链路有没有认证、加密、完整性校验、防重放措施这一步做完大部分系统的安全问题就一目了然了。我见过一些资源有限的设备确实没有条件做全套加密但通过梳理链路至少能分清主次把最关键的几条链路优先加固把实时性要求不高的链路先加 HMAC把完全无所谓的日志链路直接砍掉或收口这些都是可以接受的折中方案。通信安全不求一步到位但不能“没有清单、一片空白”。5.2 通信协议选型与参数配置通信安全在很大程度在选型阶段就已经决定了。选对了协议后面事半功倍选错了协议后面怎么补都别扭。下面是我在多个项目中总结出来的一个选型思路核心原则是“按威胁等级和数据敏感度匹配方案”。通信场景建议协议安全要求可选加固手段远程设备管理TLS over TCP / MQTT over TLS双向认证客户端证书、设备指纹管理局域网实时控制TCP 应用层加密认证完整性AES-GCM、HMAC、序列号局域网音视频/传感器UDP 应用层封装防伪防重放QUIC、序列号HMAC车载/工业总线CAN / CAN-FD认证防伪SecOC、ID白名单、网关过滤工业 Modbus485 / Modbus TCP设备认证挑战应答、寄存器加密混淆板级传感器I2C / SPI防止物理替换加密存储、MAC校验、安全启动前端组件通信事件/属性传值数据校验类型校验、白名单过滤微服务调用HTTP/RPC双向认证鉴权mTLS、服务网格、ACL参数配置方面串口通信要把波特率、数据位、校验位、停止位这四个参数按两端规格书统一CAN 总线要正确配置波特率和终端电阻120 欧姆且只在总线两端各接一个否则会出现偶发性 Bus Off网络通信要在防火墙上只放行必要的端口和来源 IPUDP 要设置合理的接收缓冲区大小防止高并发超过内核缓冲区导致丢包。很多通信故障的根源不是安全攻击而是基础参数没配好优先级排在安全加固之前。5.3 用网络调试助手和 Wireshark 做自查当你怀疑通信链路有问题时我建议先用网络调试助手做功能验证再用 Wireshark 做协议分析。网络调试助手的优势是简单直接一端作为 TCP Server 或 UDP 接收端另一端作为 Client 发数据能快速判断“链路是否通”。但它的缺点也很明显——只能看到应用层的数据内容看不到底层协议细节不能帮你分析握手失败、重传、乱序、丢包这类问题。Wireshark 才是排查通信问题的核心工具。第一次使用它的人可能会被满屏的包吓到但只需要掌握几个基础过滤条件就足够了用ip.addr 192.168.1.100过滤指定设备用tcp.port 80过滤端口用modbus、can之类的协议名直接过滤协议报文抓到包以后点击报文逐层看 Ethernet、IP、TCP/UDP 和负载字段。我排查通信故障时几乎不看应用日志而是直接看抓包里的序列号变化、ACK 回包、重传情况因为协议栈的状态永远不会骗人。自查安全问题时建议重点看三类现象第一明文传输的敏感字段是否直接在包里可见第二报文中是否缺少序列号或时间戳字段第三通信两端是否有身份标识且能被对端验证。如果这三个问题里有一个回答是“否”就说明这条链路还处于裸奔状态。6. 网络安全学习路线与常见问题排查6.1 新手入门从通信基础到安全攻防的学习路径很多想学网络安全的新人一上来就到处找“漏洞靶场”“渗透工具”结果连 TCP 三次握手、ARP 协议、HTTP 报文结构都说不清楚越学越虚。我自己带过不少新人摸索下来比较有效的路线是先吃透网络基础再做协议分析然后转向安全原理最后才进入实战和漏洞挖掘。具体来说第一阶段是通信基础把 TCP/IP 协议栈体系学明白知道 TCP 和 UDP 的区别理解三次握手、四次挥手、IP 分片、MTU、ARP、DNS 这些基础概念最好能亲手用网络调试助手收发一遍数据再用 Wireshark 抓包观察报文结构。第二阶段是编程基础Python 或 Go 二选一至少要会写 socket 客户端和服务端脚本、能解析二进制报文、能写自动化测试脚本。第三阶段是安全原理身份认证、加密算法对称/非对称、哈希与 HMAC、TLS 握手流程这些知识是后续所有安全工作的根基。第四阶段才是方向选择Web 安全可以学 OWASP Top 10、SQL 注入、XSS、CSRF二进制安全要学汇编、逆向工程、漏洞利用工控和物联网安全则要深入 CAN、Modbus、固件逆向。关于 SRC安全响应中心和漏洞挖据平台我的看法是它们是很好的“练手场”但前提是你已经掌握前面那些基础。补天、漏洞盒子、教育 SRC 等平台上有大量真实业务系统允许白帽在授权范围内提交漏洞换取积分和奖励。但请务必牢记一切测试都必须遵守平台规则和法律法规只能在授权范围内操作绝不触碰未授权目标。漏洞挖掘的本质是理解业务逻辑和协议实现中的缺陷而不是“搞破坏”这个认知决定了你能不能在这一行走得远。6.2 串口、CAN、网络通信常见故障定位做通信开发和通信安全评估最花时间的往往是排障而不是写代码。我把这些年遇到的高频故障整理成了一张速查表遇到问题可以先对照自查。现象常见原因排查/解决思路串口通信乱码波特率不匹配、数据位/停止位配置错误、TTL电平不共地用示波器或逻辑分析仪测量波形确认双方波特率完全一致STC ISP 串口下载失败冷启动时序不对、驱动未装、波特率超过芯片承受范围先断电再点击下载降低 ISP 波特率确认 P3.0/P3.1 无外部干扰CAN 总线偶发 Bus Off波特率不一致、终端电阻缺失、线缆过长、干扰确认总线两端各接 120 欧姆终端电阻检查总线占用率Modbus 通信超时从站地址错误、CRC 校验没做、轮询周期太短抓包对比请求/响应功能码和 CRC 值UDP 报文收不到防火墙拦截、端口被占用、接收缓冲区太小先关防火墙测试用 netstat 确认端口监听状态TCP 连接频繁断开保活机制未配置、中间设备清理空闲连接、序列号冲突开启 TCP KeepAlive调整应用层心跳间隔组件通信数据异常父组件传值类型变化、子组件未做校验、响应式丢失在传值入口加类型断言必要时用深拷贝隔离数据IPC 消息丢失消息队列容量满、消费者处理慢、进程崩溃调整队列长度增加消费者检查进程退出清理逻辑关于 485 通信乱码我想多说两句。它和普通串口乱码的原因不太一样很多 485 转换器是半双工方向切换的如果控制方向脚DE/RE的切换时序不对发送和接收就会互相干扰表现为“自己发出去的数据被自己收进来”“回包数据被切断”。排查时先用一个最简单的主站发轮询指令用示波器看 AB 线波形确认 A 高 B 低、静态电平在 2V 以上再看方向切换是否有毛刺。最后分享一个我自己的个人习惯调试任何通信链路时第一步永远是“抓包”而不是“看代码”。不管是串口、CAN、485 还是 TCP/UDP先把物理层的数据抓下来确认“线上到底跑了什么”再去对照代码找原因。这个习惯帮我避开过无数次“以为代码有问题其实是线序错了”“以为对端没回包其实是波特率不对”的坑。通信安全也一样在没有把链路完全看清楚之前不要急着下结论。把线上运行的每一个字节当成不可信的输入去审视通信系统的安全性就会提升一大截。
返回列表