嵌入式安全第一关:Secure Boot是怎么保护你的设备的?

嵌入式安全第一关:Secure Boot是怎么保护你的设备的?
你有没有想过一个问题如果攻击者直接拿编程器夹住你的Flash芯片把固件全部读出来你的加密算法就算再强又有什么用这不是假设。我们团队之前做过一个测试——市面上售价百元以内的几款物联网终端设备超过六成没有做任何物理层面的安全防护。一把夹子、一条杜邦线、一个半小时固件就能完整dump出来。很多人把嵌入式安全简单等同于在代码里用AES加密数据但在这个连Flash都可以热风枪吹下来放在编程器上读的物理世界里代码加密的前提是——别人读不到你的代码。所以要理解嵌入式安全首先得理解一个概念信任根。先看一段典型的MCU启动流程void boot_sequence(void) { uint32_t *app_entry (uint32_t *)APP_START_ADDR; // 1. 读取复位向量 if (*app_entry ! 0xFFFFFFFF) { // 2. 计算当前固件哈希 uint8_t hash[32]; sha256_calculate(APP_START_ADDR, APP_SIZE, hash); // 3. 与存储在OTP区域的签名对比 if (memcmp(hash, (void *)OTP_HASH_ADDR, 32) 0) { // 4. 签名验证通过正常启动 jump_to_app(app_entry); } else { // 固件被篡改进入安全模式 enter_safe_mode(); } } }这段代码呈现的是一个典型的Secure Boot流程但关键不在于代码怎么写而在于OTP区域的签名是怎么来的。OTP全称One Time Programmable是信任链的第一环。芯片出厂时厂商在OTP区域烧录一个公钥哈希或者设备证书。这个区域只能写一次且物理上无法被外部接口读取。信任从这里开始——如果连OTP的内容都可以被篡改那么整个安全体系就崩塌了。真正的问题是OTP里的根密钥怎么保证安全一个有意思的设计是很多MCU厂商在芯片内部集成了一个硬件不可克隆函数英文叫Physically Unclonable Function简称PUF。PUF利用芯片制造过程中硅片掺杂浓度的微小差异来生成一个唯一的、不可复制的指纹。这个指纹被用来派生加密密钥密钥从不出芯片只在需要时由PUF实时生成。绕了一圈本质逻辑其实是与其拼命保护一把密钥不如让这把密钥根本就不存在。不存在的东西自然偷不走。再往上一层看即使Secure Boot和无密钥存储都做好了还有另一个常被忽视的环节——固件更新通道。/* 不安全的固件更新 */ if (receive_packet(pkt) SUCCESS) { write_flash(UPDATE_ADDR, pkt.data, pkt.len); reboot(); } /* 稍微可靠的版本 */ if (receive_packet(pkt) SUCCESS) { if (verify_signature(pkt.data, pkt.len, pkt.sig) VALID) { if (decrypt_in_place(pkt.data, pkt.len, session_key) OK) { write_flash(UPDATE_ADDR, pkt.data, pkt.len); reboot(); } } }两种实现的差别不只是多几行if判断。前一种在OTA升级时任何人只要在无线信号范围内发送一个伪造的固件包就能让所有设备变砖——更糟糕的是如果攻击者植入一个恶意固件设备就成了别人手里的僵尸。后一种实现多做了两件事签名验证保证固件来源可信会话密钥解密保证传输过程中不会被窃听。这里使用session_key而不是固定密钥的原因是——如果设备所有者的通信都用同一把对称密钥加密密钥在设备出厂后就无法更换一旦泄漏就全盘皆输。从信任根出发到Secure Boot验证再到安全固件更新这三层构成了一条完整的信任链。但有个容易被忽略的细节信任链的强度取决于最弱的一环。如果生产环节中固件烧录器可以直接跳过签名验证那前面所有工作都白做了。生产安全本身就是一个不小的课题。