文章目录
-
- 每日一句正能量
- 引言:当IoT设备成为攻击目标
- 一、信任链架构:从BootROM到应用固件
-
- [1.1 Chain of Trust 设计哲学](#1.1 Chain of Trust 设计哲学)
- [1.2 为什么需要两层验证?](#1.2 为什么需要两层验证?)
- 二、eFuse:一次性可编程的安全基石
-
- [2.1 eFuse Block Layout](#2.1 eFuse Block Layout)
- [2.2 BLOCK0关键安全字段](#2.2 BLOCK0关键安全字段)
- 三、Flash加密:AES-256-XTS透明加密
-
- [3.1 硬件加密引擎架构](#3.1 硬件加密引擎架构)
- [3.2 XTS-AES加密模式](#3.2 XTS-AES加密模式)
- [3.3 首次启动加密过程](#3.3 首次启动加密过程)
- [四、Secure Boot:数字签名验证](#四、Secure Boot:数字签名验证)
-
- [4.1 Secure Boot V1 vs V2](#4.1 Secure Boot V1 vs V2)
- [4.2 签名验证流程(以V2为例)](#4.2 签名验证流程(以V2为例))
- [4.3 密钥生成与管理](#4.3 密钥生成与管理)
- 五、启用流程:从开发到量产
-
- [5.1 完整启用工作流](#5.1 完整启用工作流)
- [5.2 ESP-IDF配置步骤](#5.2 ESP-IDF配置步骤)
- [5.3 开发模式 vs 发布模式](#5.3 开发模式 vs 发布模式)
- 六、威胁模型与防护效果分析
-
- [6.1 攻击场景与防护能力](#6.1 攻击场景与防护能力)
- [6.2 已知攻击与缓解措施](#6.2 已知攻击与缓解措施)
- 七、生产环境安全部署最佳实践
-
- [7.1 四层安全架构](#7.1 四层安全架构)
- [7.2 完整安全启动代码示例](#7.2 完整安全启动代码示例)
- [7.3 partition.csv安全配置](#7.3 partition.csv安全配置)
- 八、常见问题与故障排查
- 九、总结

每日一句正能量
沉溺于过去,在损失中纠缠太久,只会失去更多,着眼于未来,找准自己的优势,才能逆风翻盘。
过去的损失已是沉没成本。纠缠的唯一结果,是赔上现在和未来。翻盘的关键不是弥补过去,而是用优势去打下一局。
引言:当IoT设备成为攻击目标
2025年,全球IoT设备数量突破300亿台,其中基于ESP32的智能家居、工业网关、医疗设备占据了相当大的份额。这些设备往往部署在物理上不可控的环境中------用户的客厅、工厂的角落、医院的病房。一旦攻击者能够物理接触设备,传统的软件安全防线便显得脆弱不堪。
ESP32作为市面上最主流的Wi-Fi/蓝牙双模MCU之一,从硬件层面提供了两道关键的安全防线:Secure Boot(安全启动)和Flash Encryption(Flash加密)。前者确保只有经过授权的固件才能运行,后者确保即使Flash芯片被物理拆下读取,也无法获得有效数据。本文将从信任链架构、数字签名算法、密钥管理到生产部署实践,深度解析ESP32的安全启动机制。
一、信任链架构:从BootROM到应用固件
1.1 Chain of Trust 设计哲学
ESP32的安全启动遵循经典的**信任链(Chain of Trust)**设计:每一层软件在加载下一层之前,必须先验证其数字签名。信任的根源(Root of Trust)是固化在芯片Mask ROM中的BootROM------这是物理上不可篡改的代码。

信任链的验证流程:
- BootROM(硬件信任根):芯片上电后,BootROM首先执行。它从eFuse中读取Secure Boot Key(SBK),使用该密钥验证Flash中存储的Second Stage Bootloader的签名。
- Second Stage Bootloader:验证通过后,Bootloader加载并执行。它继续使用相同的公钥验证Application Firmware(应用固件)的签名。
- Application Firmware:只有所有验证通过,应用固件才会执行。任一环节失败,设备将拒绝启动。
1.2 为什么需要两层验证?
这种"BootROM验证Bootloader,Bootloader验证App"的设计并非冗余,而是**纵深防御(Defense in Depth)**的体现:
- 如果仅验证应用固件,攻击者可以替换Bootloader,在应用固件加载前植入恶意代码
- 两层验证确保从芯片上电到应用执行的整个链条都是可信的
二、eFuse:一次性可编程的安全基石
2.1 eFuse Block Layout
eFuse(电子熔丝)是ESP32内部的一次性可编程(OTP)存储器,共4个256位Block(1024位总计)。它是所有安全功能的物理基础。

| Block | 名称 | 用途 | 保护机制 |
|---|---|---|---|
| BLOCK0 | System | MAC地址、CPU频率、Flash电压、安全标志位 | 部分字段可写 |
| BLOCK1 | Flash Encryption Key | 256-bit AES密钥,用于Flash加密 | WR_DIS + RD_DIS |
| BLOCK2 | Secure Boot Key | 公钥哈希或密钥,用于签名验证 | WR_DIS + RD_DIS |
| BLOCK3 | User/Custom | 自定义MAC、ADC校准数据、用户数据 | 可选保护 |
2.2 BLOCK0关键安全字段
| 字段名 | 位数 | 功能 |
|---|---|---|
FLASH_CRYPT_CNT |
7-bit | Flash加密计数器,奇数个1表示加密已启用 |
FLASH_CRYPT_CONFIG |
4-bit | 加密配置,控制密钥tweak的位数 |
ABS_DONE_0 |
1-bit | Secure Boot V1启用标志 |
ABS_DONE_1 |
1-bit | Secure Boot V2启用标志 |
DIS_DOWNLOAD_MODE |
1-bit | 禁用UART下载模式(Release Mode必需) |
JTAG_DISABLE |
1-bit | 禁用JTAG调试接口 |
WR_DIS_BLK1 |
1-bit | BLOCK1写保护(防止密钥被覆盖) |
RD_DIS_BLK1 |
1-bit | BLOCK1读保护(防止密钥被读取) |
核心原则 :密钥烧录到eFuse后,必须立即设置WR_DIS(写保护)和RD_DIS(读保护)。一旦设置,即使是芯片上的软件也无法读取或修改这些密钥------这是硬件级别的隔离。
三、Flash加密:AES-256-XTS透明加密
3.1 硬件加密引擎架构
ESP32内置专用的AES-256硬件加密引擎,位于Flash控制器和CPU之间。所有Flash读写操作都经过该引擎的透明加解密。

工作流程:
- 写入:CPU写入明文数据 → Flash缓存 → AES引擎加密 → SPI Flash存储密文
- 读取:SPI Flash读取密文 → AES引擎解密 → Flash缓存 → CPU获得明文
- 密钥来源:256-bit AES密钥(FEK)从eFuse BLOCK1自动加载到AES引擎,软件无法直接访问
3.2 XTS-AES加密模式
ESP32使用XTS-AES模式进行Flash加密,这是IEEE 1619标准定义的磁盘加密模式,相比标准AES-ECB/CBC具有以下优势:
c
// XTS-AES加密公式 (简化)
// C_i = AES_K1(P_i XOR T_i) XOR T_i
// 其中 T_i = AES_K2(i) * α^j
// i = 扇区号, j = 块内偏移, α = 伽罗瓦域本原元
// 这意味着:
// 1. 每个32字节块使用不同的tweak值(基于Flash偏移地址)
// 2. 相同明文在不同位置产生不同密文
// 3. 密文篡改会导致整个扇区解密失败(数据完整性保护)
加密范围:
- ✅ Bootloader
- ✅ Partition Table
- ✅ 所有"app"类型分区
- ✅ 标记了
encrypted标志的分区 - ❌ NVS分区(因flash友好型更新算法会失效)
- ❌ 未标记的数据分区
3.3 首次启动加密过程
当Flash加密启用后,首次启动时Bootloader会自动执行以下操作:
bash
# 1. 检查 FLASH_CRYPT_CNT
# 如果为0(偶数个1),表示加密未启用
# 2. 生成随机256-bit FEK
# 使用硬件真随机数生成器(TRNG)
# 密钥自动写入eFuse BLOCK1
# 3. 原地加密所有受保护分区
# 读取明文 → AES加密 → 写回密文
# 此过程约需30秒(取决于Flash大小)
# 4. 更新 FLASH_CRYPT_CNT
# 从0变为1(奇数,表示加密已启用)
# 5. 重启设备
# 从加密后的Flash正常启动
⚠️ 致命警告 :首次启动加密过程中绝不能断电。如果中断,Flash内容将损坏,需要重新刷写明文固件。
四、Secure Boot:数字签名验证
4.1 Secure Boot V1 vs V2
ESP32支持两个版本的Secure Boot,核心差异在于算法和密钥管理:

| 特性 | Secure Boot V1 | Secure Boot V2 |
|---|---|---|
| 适用芯片 | ESP32 (ECO1-2), ESP32-S2 | ESP32-S3, ESP32-C3, ESP32-C6, ESP32-H2 |
| 签名算法 | RSA-3072 | RSA-3072 或 ECDSA-P256 |
| 密钥数量 | 单签名密钥 | 最多3个签名密钥(支持密钥轮换) |
| 公钥存储 | eFuse BLOCK2(完整公钥) | eFuse KEY0-KEY5(公钥哈希) |
| 摘要算法 | SHA-256 | SHA-256 (RSA-PSS) |
| OTA密钥轮换 | ❌ 不支持 | ✅ 支持 |
| 安全级别 | ⭐⭐⭐ | ⭐⭐⭐⭐⭐ |
4.2 签名验证流程(以V2为例)
c
// Secure Boot V2 签名验证流程(BootROM层面)
// 1. 从Flash偏移0x0读取Bootloader
// 签名块位于Bootloader末尾
// 2. 解析签名块结构
// typedef struct {
// uint8_t magic_byte; // 0xE7
// uint8_t version; // 0x02
// uint16_t reserved;
// uint8_t fingerprints[3][32]; // 3个公钥指纹(SHA-256)
// uint8_t signatures[3][384]; // 3个RSA-3072签名
// } esp_secure_boot_sig_block_t;
// 3. 计算Bootloader的SHA-256摘要
// digest = SHA-256(bootloader_image)
// 4. 对每个签名进行验证
// for i in 0..2:
// pubkey_hash = SHA-256(pubkey[i])
// if pubkey_hash matches eFuse KEY[i]:
// verify RSA-PSS-Sign(digest, signature[i], pubkey[i])
// if valid: boot allowed
// 5. 如果所有签名验证失败
// → 拒绝启动,进入安全状态
4.3 密钥生成与管理
bash
# 1. 生成Secure Boot签名密钥对 (RSA-3072)
espsecure.py generate_signing_key --version 2 secure_boot_signing_key.pem
# 2. 生成Flash加密密钥 (256-bit随机数)
espsecure.py generate_flash_encryption_key flash_encryption_key.bin
# 3. 查看公钥信息
espsecure.py digest_rsa_publickey --keyfile secure_boot_signing_key.pem pubkey_digest.bin
# 4. 固件签名 (编译后)
espsecure.py sign_data --version 2 --keyfile secure_boot_signing_key.pem \
--output signed_firmware.bin firmware.bin
密钥安全原则:
- 私钥(
secure_boot_signing_key.pem)必须离线保管,绝不进入版本控制系统 - 生产环境应使用**硬件安全模块(HSM)**或专用签名服务器
- 开发、测试、生产环境应使用不同的签名密钥
五、启用流程:从开发到量产
5.1 完整启用工作流

5.2 ESP-IDF配置步骤
Step 1: menuconfig配置
bash
idf.py menuconfig
# 导航路径:
# → Security features
# → Enable hardware Secure Boot in bootloader (选择V2)
# → Sign binaries during build (启用自动签名)
# → Secure boot signing key (指定私钥路径)
# → Enable flash encryption on boot (启用Flash加密)
# → Enable usage mode (Development / Release)
Step 2: 构建并烧录
bash
# 构建项目 (自动签名)
idf.py build
# 烧录明文固件 (首次烧录必须是明文)
idf.py flash
# 监控首次启动加密过程
idf.py monitor
# 输出: "Flash encryption completed, resetting..."
Step 3: 验证加密状态
bash
# 读取Flash内容验证加密
esptool.py --port /dev/ttyUSB0 read_flash 0 0x400000 flash_backup.bin
# 使用hexdump检查 - 应显示随机数据
hexdump -C flash_backup.bin | head -20
# 预期输出: 全是随机字节,无可读字符串
Step 4: 切换到Release Mode(生产前)
bash
# ⚠️ 此操作不可逆!确保OTA机制已完全测试!
# 禁用UART下载模式
espefuse.py --port /dev/ttyUSB0 burn_efuse DIS_DOWNLOAD_MODE 1
# 禁用JTAG调试
espefuse.py --port /dev/ttyUSB0 burn_efuse JTAG_DISABLE 1
# 写保护所有安全eFuse
espefuse.py --port /dev/ttyUSB0 write_protect_efuse BLOCK0
5.3 开发模式 vs 发布模式
| 特性 | Development Mode | Release Mode |
|---|---|---|
| UART下载 | ✅ 可用 | ❌ 永久禁用 |
| JTAG调试 | ✅ 可用 | ❌ 永久禁用 |
| 物理重烧录 | ✅ 有限次数(~4次) | ❌ 不可行 |
| OTA更新 | ✅ 可用 | ✅ 唯一更新方式 |
| 安全级别 | 中 | 最高 |
| 适用场景 | 开发调试 | 量产部署 |
关键警告:在启用Release Mode之前,必须:
- 验证OTA机制至少成功运行5次
- 测试密钥轮换流程(V2支持)
- 确认有可靠的固件回滚机制
- 备份所有密钥到安全存储
六、威胁模型与防护效果分析
6.1 攻击场景与防护能力

核心洞察 :Secure Boot和Flash Encryption是互补的,而非冗余的。
- Secure Boot解决:谁能运行代码(身份认证 + 完整性校验)
- Flash Encryption解决:代码和数据在静止状态下是否可读(机密性保护)
单独启用任何一个,都会留下攻击面。只有两者同时启用,才能形成完整的纵深防御体系。
6.2 已知攻击与缓解措施
| 攻击类型 | 描述 | 缓解措施 |
|---|---|---|
| 电压故障注入 | 通过精确控制电源毛刺绕过验证检查 | 启用Release Mode,物理加固电源 |
| 侧信道分析 | 通过功耗/电磁辐射分析提取密钥 | 使用新一代芯片(ECO3+),硬件防护增强 |
| JTAG调试攻击 | 通过调试接口读取内存/控制执行 | 烧录JTAG_DISABLE eFuse |
| UART下载攻击 | 通过串口刷入未签名固件 | 烧录DIS_DOWNLOAD_MODE eFuse |
| Flash物理读取 | 拆下Flash芯片用编程器读取 | Flash Encryption(密文不可读) |
CVE-2019-17391 :2019年披露的安全漏洞表明,早期ESP32芯片(ECO1-2)存在电压故障注入攻击风险,攻击者可通过精确控制VDD_CPU和VDD_RTC电源毛刺,在eFuse加载期间读取受保护的密钥。该漏洞已在ECO3及后续芯片中修复,建议新设计优先使用ECO3+版本。
七、生产环境安全部署最佳实践
7.1 四层安全架构

Layer 1: 密钥生成
- 在**离线(air-gapped)**机器上生成所有密钥
- 使用硬件安全模块(HSM)或专用安全芯片存储私钥
- 私钥绝不接触任何联网设备或开发环境
- 生成后立即备份至物理安全存储(如保险箱)
Layer 2: 密钥烧录
- 产线使用专用烧录设备,与开发网络物理隔离
- 每台设备使用唯一的Flash加密密钥(FEK),防止"一损俱损"
- 烧录后立即设置
WR_DIS和RD_DIS保护 - 记录设备序列号与密钥的映射关系(用于售后维护)
Layer 3: 固件签名
- CI/CD流水线自动签名,私钥存储在专用签名服务器
- 利用Secure Boot V2的多密钥特性,支持密钥轮换
- 签名前进行代码审计、静态分析和漏洞扫描
- 保留签名日志用于审计追踪
Layer 4: 设备加固
- 启用
DIS_DOWNLOAD_MODE禁用UART下载 - 启用
JTAG_DISABLE禁用调试接口 - 启用
DIS_LEGACY_SPI_BOOT禁用传统SPI启动 - 配置看门狗和防回滚机制
7.2 完整安全启动代码示例
c
// main.c - ESP32 Secure Boot + Flash Encryption 安全初始化
#include "esp_system.h"
#include "esp_log.h"
#include "esp_secure_boot.h"
#include "esp_flash_encrypt.h"
static const char *TAG = "SECURE_BOOT";
void security_init(void)
{
// 1. 检查Secure Boot状态
#ifdef CONFIG_SECURE_BOOT_V2_ENABLED
esp_err_t sb_status = esp_secure_boot_verify_signature(NULL, 0);
if (sb_status != ESP_OK) {
ESP_LOGE(TAG, "Secure Boot verification failed! Halting.");
esp_restart(); // 安全状态:重启
}
ESP_LOGI(TAG, "Secure Boot V2: VERIFIED");
#endif
// 2. 检查Flash加密状态
#ifdef CONFIG_SECURE_FLASH_ENC_ENABLED
bool flash_enc_enabled = esp_flash_encryption_enabled();
if (!flash_enc_enabled) {
ESP_LOGW(TAG, "Flash encryption not enabled!");
// 在开发模式下允许继续,生产环境应拒绝启动
#ifdef CONFIG_SECURE_FLASH_ENC_RELEASE_MODE
ESP_LOGE(TAG, "Flash encryption required in release mode! Halting.");
esp_restart();
#endif
} else {
ESP_LOGI(TAG, "Flash Encryption: ENABLED");
}
#endif
// 3. 检查eFuse保护状态
uint32_t dis_download = REG_READ(EFUSE_RD_REG_BASE);
if (dis_download & EFUSE_RD_DIS_DOWNLOAD_MODE) {
ESP_LOGI(TAG, "UART Download Mode: DISABLED");
} else {
ESP_LOGW(TAG, "UART Download Mode: ENABLED (development only)");
}
// 4. 运行时完整性检查(可选)
// 定期验证关键代码段的哈希值
// 检测运行时篡改
}
void app_main(void)
{
// 安全初始化必须在任何业务逻辑之前
security_init();
// 业务逻辑...
ESP_LOGI(TAG, "Application started securely");
}
7.3 partition.csv安全配置
csv
# Name, Type, SubType, Offset, Size, Flags
nvs, data, nvs, 0x9000, 0x6000,
otadata, data, ota, 0xf000, 0x2000,
app0, app, ota_0, 0x10000, 0x1F0000, encrypted
app1, app, ota_1, 0x200000,0x1F0000, encrypted
spiffs, data, spiffs, 0x3F0000,0x10000, encrypted
关键配置:
app分区自动加密,无需显式标记nvs分区不可加密(标记encrypted会导致NVS库失效)- 敏感数据分区(如spiffs存储的配置文件)应标记
encrypted
八、常见问题与故障排查
8.1 "设备启动后无响应"
可能原因:
- Flash LATENCY配置错误(F4系列)
- 首次启动加密过程中断电,Flash损坏
- 签名验证失败,BootROM拒绝启动
排查方法:
bash
# 强制进入下载模式(短接GPIO0到GND)
# 使用esptool读取eFuse状态
espefuse.py --port /dev/ttyUSB0 summary
# 检查FLASH_CRYPT_CNT值
# 检查ABS_DONE_1(Secure Boot V2)状态
8.2 "OTA更新后设备变砖"
可能原因:
- OTA固件未使用正确的私钥签名
- 新固件与当前Secure Boot密钥不匹配
- 分区表变更导致签名块位置错误
预防措施:
- 在Development Mode下至少测试5次OTA更新
- 使用Secure Boot V2的密钥轮换功能时,确保新密钥已烧录到eFuse
- OTA更新前验证固件签名的有效性
8.3 "如何更换签名密钥?"
Secure Boot V1:无法更换。一旦密钥烧录,设备永久绑定该密钥。
Secure Boot V2:支持密钥轮换。最多3个密钥槽位,可以在保留旧密钥的同时引入新密钥,实现平滑过渡。
bash
# V2密钥轮换流程
# 1. 生成新密钥
espsecure.py generate_signing_key --version 2 new_signing_key.pem
# 2. 将新公钥哈希烧录到下一个KEY槽位
espefuse.py burn_key BLOCK_KEY1 new_pubkey_digest.bin
# 3. 使用新密钥签名固件
espsecure.py sign_data --version 2 --keyfile new_signing_key.pem firmware.bin
# 4. OTA推送新固件
# 设备将使用新密钥验证签名
九、总结
ESP32的Secure Boot和Flash Encryption构成了一套从硬件到软件的完整安全体系。理解其工作原理并正确配置,是保护IoT设备免受物理攻击和固件篡改的关键。
核心要点回顾:
-
Secure Boot确保代码可信:通过RSA-3072/ECDSA数字签名,建立从BootROM到应用固件的信任链。
-
Flash Encryption确保数据机密:通过AES-256-XTS透明加密,即使物理拆下Flash也无法读取敏感数据。
-
eFuse是安全根基:所有密钥存储在OTP存储器中,烧录后必须立即设置读写保护。
-
Release Mode是最终防线:禁用UART下载和JTAG调试,将设备锁定为仅OTA更新模式。
-
密钥管理是最大风险:私钥的泄露意味着整个产品线的安全崩溃。离线生成、HSM存储、分级管理是最佳实践。
正如Espressif安全团队所强调的:"安全不是功能,而是过程。" 从密钥生成到设备部署的每一个环节,都需要严格的安全意识和规范的操作流程。
转载自:https://blog.csdn.net/u014727709/article/details/162275528
欢迎 👍点赞✍评论⭐收藏,欢迎指正