Table of Contents

Laborator 5: Securizarea unui proiect embedded pe ESP32-CAM

În acest laborator ne vom concentra pe securizarea completă a sistemului, de la layer-ul de transport (TLS) până la codul sursă și hardware-ul propriu-zis.

Obiective

  1. Implementarea comunicației criptate (MQTT peste TLS).
  2. Analiza securității codului folosind unelte automate (bandit, cppcheck).
  3. Mecanisme de protecție a firmware-ului (Secure Boot, Flash Encryption).

Partea 1: Securizarea transportului (TLS/SSL)

Până acum, traficul MQTT (inclusiv imaginile) circulă în clar prin rețea. Oricine conectat la aceeași rețea poate intercepta datele cu un simplu Wireshark. Vom securiza comunicația folosind TLS (Transport Layer Security).

1.1 Generarea certificatelor (OpenSSL)

Vom crea o Autoritate de Certificare (CA) proprie care va semna certificatele serverului.

Modern OpenSSL validează adresele IP prin câmpul Subject Alternative Name (SAN), nu doar prin Common Name (CN). Comenzile de mai jos includ acest câmp.

Creați un director certs în rădăcina proiectului:

mkdir certs && cd certs

Pasul 1: Generarea CA (Certificate Authority)

# Cheia privată a CA
openssl genrsa -out ca.key 2048
 
# Certificatul CA (auto-semnat, valabil 10 ani)
openssl req -x509 -new -nodes -key ca.key -sha256 -days 3650 -out ca.crt \
    -subj "/CN=SS-Project-CA"

Pasul 2: Generarea certificatului pentru broker (Server)

Înlocuiți ADRESA_IP_BROKER cu IP-ul real al mașinii pe care rulează Mosquitto (ex: 192.168.1.100):

# Cheia privată a serverului
openssl genrsa -out server.key 2048
 
# Cererea de semnare (CSR) cu Subject Alternative Name
openssl req -new -key server.key -out server.csr \
    -subj "/CN=ADRESA_IP_BROKER" \
    -addext "subjectAltName=IP:ADRESA_IP_BROKER"
 
# Semnarea certificatului de către CA (valabil 1 an)
openssl x509 -req -in server.csr \
    -CA ca.crt -CAkey ca.key -CAcreateserial \
    -out server.crt -days 365 -sha256 \
    -copy_extensions copyall

Verificare

# Verificați că certificatul serverului conține SAN-ul corect
openssl x509 -in server.crt -text -noout | grep -A1 "Subject Alternative Name"
# Ar trebui să vedeți: IP Address:ADRESA_IP_BROKER

1.2 Configurarea Mosquitto cu TLS

Creați un fișier mosquitto_secure.conf:

mosquitto_secure.conf
listener 8883
cafile ./certs/ca.crt
certfile ./certs/server.crt
keyfile ./certs/server.key
require_certificate false

Porniți brokerul securizat:

sudo systemctl stop mosquitto.service
mosquitto -c mosquitto_secure.conf -v

Ar trebui să vedeți în log: Opening ipv4 listen socket on port 8883.

1.3 Actualizarea receiver-ului Python

Adăugați o singură linie înainte de client.connect():

# Activăm TLS cu certificatul CA
client.tls_set(ca_certs="certs/ca.crt")
 
# Schimbăm portul la 8883
client.connect(BROKER, 8883, 60)

Testați: rulați receiver-ul și verificați că se conectează fără erori.

1.4 Actualizarea firmware-ului ESP32

Modificați main.cpp pentru a folosi WiFiClientSecure:

main.cpp
#include <WiFiClientSecure.h>  // Înlocuiește <WiFi.h> pentru TLS
 
// Copiați conținutul fișierului ca.crt aici (inclusiv BEGIN/END):
const char* ca_cert = \
"-----BEGIN CERTIFICATE-----\n" \
"MIIDaz... (conținutul certificatului vostru) ...\n" \
"-----END CERTIFICATE-----\n";
 
// Înlocuiți WiFiClient cu WiFiClientSecure
WiFiClientSecure espClient;
 
void setup() {
  // ... (după conectarea la WiFi)
 
  // Încărcăm certificatul CA
  espClient.setCACert(ca_cert);
 
  // Portul devine 8883
  client.setServer(mqtt_server, 8883);
  // ...
}

Debugging: Dacă ESP32 refuză conexiunea cu SSL handshake failed, verificați:

  1. CN/SAN-ul certificatului corespunde IP-ului brokerului.
  2. Ceasul ESP32 este aproximativ corect (certificatele au dată de expirare).
  3. Ca test temporar, puteți folosi espClient.setInsecure() pentru a dezactiva validarea.

Partea 2: Analiza statică a codului (SAST)

Folosim unelte automate pentru a găsi vulnerabilități fără a rula codul.

2.1 Bandit (Python)

Analizează codul Python pentru probleme de securitate.

pip install bandit
bandit -r receiver/

Ce caută Bandit?

2.2 Cppcheck (C/C++)

Analizează codul firmware-ului pentru bug-uri și vulnerabilități.

sudo apt-get install cppcheck
cppcheck --enable=all --inconclusive --std=c++11 camera/src/

Ce caută Cppcheck?

Partea 3: Securitatea hardware (ESP32)

Chiar dacă comunicația este criptată și codul este curat, un atacator cu acces fizic la dispozitiv poate extrage firmware-ul sau cheile TLS din memoria flash.

3.1 Flash encryption

Criptează conținutul memoriei flash (cod + date) cu o cheie AES stocată în eFuse (memorie write-once din cip, imposibil de citit prin software).

3.2 Secure Boot

Verifică semnătura digitală a firmware-ului la fiecare pornire.

3.3 Verificarea stării cu ''espefuse.py''

Putem inspecta starea de securitate a unui cip fără a modifica nimic:

espefuse.py --port /dev/ttyACM0 summary

Urmăriți secțiunea Security fuses:

PERICOL DE BRICK-UIRE PERMANENTĂ A PLĂCUȚEI DE LABORATOR!

Comenzile espefuse.py burn_efuse, espefuse.py burn_key și espefuse.py write_protect_efuse sunt IREVERSIBILE. eFuse-urile sunt memorii one-time programmable — odată scrise, nu mai pot fi modificate sau resetate. O comandă greșită va face plăcuța complet inutilizabilă (bricked).

Reguli stricte:

  1. Folosiți NUMAI comanda summary (doar citire).
  2. NU rulați NICIO altă comandă `espefuse.py` fără aprobarea asistentului.
  3. Dacă vedeți un tutorial online care vă cere să scrieți eFuse-uri, IGNORAȚI-L! Acele operații sunt pentru producția de serie, nu pentru laborator.
  4. Studenții care distrug plăcuțe prin neglijență vor fi responsabili de înlocuirea lor.

Exerciții

  1. Implementare TLS: Urmăriți pașii din Partea 1 pentru a securiza comunicația. Verificați cu Wireshark că traficul pe portul 8883 este criptat (comparativ cu portul 1883 din laboratorul anterior).
  2. Secret management: Modificați main.cpp și receiver.py astfel încât credențialele să nu mai fie hardcodate:
    • Python: Citiți din variabile de mediu (os.getenv(“MQTT_BROKER”)).
    • ESP32: Creați un fișier include/secrets.h cu credențialele și adăugați-l în .gitignore.
  3. Analiza vulnerabilității: Rulați bandit și cppcheck pe codul vostru. Documentați toate problemele găsite și remediați cele de nivel “High” și “Medium”.