Segurança em Sistemas Embarcados
Secure boot, flash encryption e proteção de firmware no ESP32.
Um ESP32 conectado à internet é um alvo. Sem proteção adequada, atacantes podem:
- Extrair seu firmware e cloná-lo
- Modificar o código para adicionar backdoors
- Roubar credenciais WiFi e APIs
- Transformar seu dispositivo em parte de uma botnet
Este capítulo cobre as proteções disponíveis no ESP32 e como implementá-las.
O Modelo de Ameaça
Ataques Físicos
- Extrair flash externa e ler conteúdo
- Conectar debugger JTAG
- Analisar firmware com ferramentas de RE
Ataques Remotos
- Explorar vulnerabilidades no código
- Man-in-the-middle em atualizações OTA
- Injetar firmware malicioso
O que Proteger
- Código fonte (IP)
- Credenciais (WiFi, API keys, certificados)
- Integridade do firmware (não modificado)
- Comunicações (TLS)
Secure Boot
Secure Boot garante que apenas firmware assinado por você possa rodar no dispositivo.
Como Funciona
- Você gera um par de chaves RSA (pública/privada)
- A chave pública é gravada no eFuse (one-time programmable)
- Seu firmware é assinado com a chave privada
- Na inicialização, o bootloader verifica a assinatura
Se a assinatura não bater, o ESP32 não executa o firmware.
Habilitando
idf.py menuconfig
# Security features -> Enable hardware Secure Boot
# Escolha: Secure Boot V2 (recomendado para ESP32-S2/S3/C3)
Gerando Chaves
# Gera chave privada (NUNCA compartilhe ou perca!)
espsecure.py generate_signing_key secure_boot_signing_key.pem
# Extrai chave pública
espsecure.py extract_public_key --keyfile secure_boot_signing_key.pem public_key.pem
Assinando Firmware
# Assina o binário
espsecure.py sign_data --keyfile secure_boot_signing_key.pem \
--output firmware-signed.bin firmware.bin
Considerações
⚠️ Irreversível: Uma vez gravada a chave no eFuse, não pode ser alterada
⚠️ Backup: Perca a chave privada = dispositivos viram tijolo
⚠️ Desenvolvimento: Use chave de desenvolvimento separada da produção
Flash Encryption
Flash Encryption criptografa o conteúdo da flash com AES-256. Mesmo que alguém extraia fisicamente o chip de flash, não consegue ler o conteúdo.
O que é Criptografado
- Bootloader
- Tabela de partições
- Aplicação
- Partições OTA
- Partições marcadas como “encrypted”
Habilitando
idf.py menuconfig
# Security features -> Enable flash encryption on boot
Modos
| Modo | Descrição |
|---|---|
| Development | Permite reflash. Limite de 3 flashes em plaintext. |
| Release | Não permite reflash em plaintext. Produção. |
Como Funciona
- Na primeira inicialização, ESP32 gera chave AES aleatória
- Chave é armazenada em eFuse (protegida)
- Bootloader criptografa toda a flash
- Leituras subsequentes são descriptografadas em hardware
eFuse FLASH_CRYPT_CNT
Este eFuse controla o estado da criptografia:
- Par: bootloader pode criptografar (plaintext upload OK)
- Ímpar: bootloader não criptografa (precisa upload criptografado)
- Máximo: 3 uploads plaintext antes de ser obrigatório criptografar
Flashing com Encryption
# Primeira vez (plaintext, será criptografado no boot)
idf.py -p COM3 flash
# Após ativação, precisa criptografar antes de flashar
espsecure.py encrypt_flash_data --keyfile flash_key.bin \
--address 0x10000 --output app-encrypted.bin app.bin
idf.py -p COM3 write_flash 0x10000 app-encrypted.bin
OTA Segura
Atualizações Over-The-Air precisam ser seguras:
Requisitos
- HTTPS: Sempre use TLS para download
- Verificação de assinatura: Firmware deve ser assinado
- Rollback: Capacidade de voltar versão se falhar
- Verificação de versão: Não aceitar downgrade
Implementação
#include "esp_https_ota.h"
esp_err_t perform_ota(const char *url) {
esp_http_client_config_t config = {
.url = url,
.cert_pem = server_cert_pem, // Certificado do servidor
};
esp_https_ota_config_t ota_config = {
.http_config = &config,
};
esp_err_t ret = esp_https_ota(&ota_config);
if (ret == ESP_OK) {
esp_restart();
}
return ret;
}
Anti-Rollback
idf.py menuconfig
# Bootloader config -> Enable app rollback support
# Security features -> Enable anti-rollback
O eFuse armazena a versão mínima. Firmware com versão menor é rejeitado.
NVS Encryption
Non-Volatile Storage (NVS) guarda configurações. Por padrão, não é criptografado.
idf.py menuconfig
# Component config -> NVS -> Enable NVS encryption
#include "nvs_flash.h"
// Inicializa NVS com encryption
esp_err_t err = nvs_flash_init();
if (err == ESP_ERR_NVS_NO_FREE_PAGES) {
ESP_ERROR_CHECK(nvs_flash_erase());
err = nvs_flash_init();
}
Proteções Adicionais
Desabilitar JTAG
idf.py menuconfig
# Security features -> Disable hardware JTAG
Uma vez desabilitado via eFuse, não pode ser reativado.
Desabilitar UART Download Mode
# Security features -> UART ROM download mode -> Disabled
Impede flash via UART após secure boot ativo.
Protect eFuses
# Após configurar, proteja leitura dos eFuses sensíveis
espefuse.py burn_efuse DISABLE_JTAG
espefuse.py write_protect_efuse FLASH_CRYPT_CNT
Checklist de Segurança para Produção
| Item | Status |
|---|---|
| Secure Boot V2 habilitado | ☐ |
| Flash Encryption em modo Release | ☐ |
| JTAG desabilitado | ☐ |
| UART download mode desabilitado | ☐ |
| NVS encryption habilitado | ☐ |
| OTA apenas via HTTPS | ☐ |
| Firmware assinado | ☐ |
| Anti-rollback configurado | ☐ |
| Chaves de produção separadas | ☐ |
| Backup seguro das chaves | ☐ |
| eFuses protegidos contra leitura | ☐ |
Desenvolvimento vs Produção
| Aspecto | Desenvolvimento | Produção |
|---|---|---|
| Secure Boot | Opcional ou dev key | Obrigatório, prod key |
| Flash Encryption | Development mode | Release mode |
| JTAG | Habilitado | Desabilitado |
| UART Boot | Habilitado | Desabilitado |
| OTA | HTTP OK para testes | Apenas HTTPS |
Nunca envie dispositivos com configuração de desenvolvimento.
Referências:
- Espressif. ESP-IDF Security Guide
- Espressif. ESP32 Technical Reference Manual, Security Chapter
- OWASP. IoT Security Verification Standard