This shows you the differences between two versions of the page.
|
ss:laboratoare:05 [2025/03/03 19:48] jan.vaduva [Resurse suplimentare] |
ss:laboratoare:05 [2026/03/30 19:39] (current) ciprian.popescu0411 [Exerciții] |
||
|---|---|---|---|
| Line 1: | Line 1: | ||
| - | ====== Laborator 5: Analiza statică a codului ====== | + | ====== 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 ===== | ===== Obiective ===== | ||
| - | * Aplicarea unor instrumente de analiză statică pentru identificarea vulnerabilităților din codul aplicației dezvoltate în laboratoarele anterioare | ||
| - | * Detectarea și remedierea problemelor de securitate, performanță și calitate a codului | ||
| - | * Integrarea analizei statice în pipeline-ul CI/CD pentru prevenirea introducerii de vulnerabilități | ||
| - | ===== Cerințe tehnologice ===== | + | - Implementarea comunicației criptate (MQTT peste TLS). |
| - | * **Instrumente de analiză statică**: SonarQube, Bandit (Python), ESLint (JavaScript), SpotBugs (Java) | + | - Analiza securității codului folosind unelte automate (''bandit'', ''cppcheck''). |
| - | * **CI/CD Pipeline**: GitHub Actions/GitLab CI/CD/Jenkins pentru rularea automată a analizei | + | - Mecanisme de protecție a firmware-ului (Secure Boot, Flash Encryption). |
| - | * **Reguli de securitate**: OWASP Top 10, CWE (Common Weakness Enumeration) | + | |
| - | * **Format de raportare**: SARIF, JSON, HTML | + | ===== 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. | ||
| + | |||
| + | <note important> | ||
| + | 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. | ||
| + | </note> | ||
| + | |||
| + | Creați un director ''certs'' în rădăcina proiectului: | ||
| + | |||
| + | <code bash> | ||
| + | mkdir certs && cd certs | ||
| + | </code> | ||
| + | |||
| + | === Pasul 1: Generarea CA (Certificate Authority) === | ||
| + | |||
| + | <code bash> | ||
| + | # 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" | ||
| + | </code> | ||
| + | |||
| + | === 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''): | ||
| + | |||
| + | <code bash> | ||
| + | # 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 | ||
| + | </code> | ||
| + | |||
| + | === Verificare === | ||
| + | |||
| + | <code bash> | ||
| + | # 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 | ||
| + | </code> | ||
| + | |||
| + | ==== 1.2 Configurarea Mosquitto cu TLS ==== | ||
| + | |||
| + | Creați un fișier ''mosquitto_secure.conf'': | ||
| + | |||
| + | <file bash mosquitto_secure.conf> | ||
| + | listener 8883 | ||
| + | cafile ./certs/ca.crt | ||
| + | certfile ./certs/server.crt | ||
| + | keyfile ./certs/server.key | ||
| + | require_certificate false | ||
| + | </file> | ||
| + | |||
| + | Porniți brokerul securizat: | ||
| + | |||
| + | <code bash> | ||
| + | sudo systemctl stop mosquitto.service | ||
| + | mosquitto -c mosquitto_secure.conf -v | ||
| + | </code> | ||
| + | |||
| + | 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()'': | ||
| + | |||
| + | <code python> | ||
| + | # Activăm TLS cu certificatul CA | ||
| + | client.tls_set(ca_certs="certs/ca.crt") | ||
| + | |||
| + | # Schimbăm portul la 8883 | ||
| + | client.connect(BROKER, 8883, 60) | ||
| + | </code> | ||
| + | |||
| + | 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'': | ||
| + | |||
| + | <file cpp 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); | ||
| + | // ... | ||
| + | } | ||
| + | </file> | ||
| + | |||
| + | <note> | ||
| + | **Debugging**: Dacă ESP32 refuză conexiunea cu ''SSL handshake failed'', verificați: | ||
| + | - CN/SAN-ul certificatului corespunde IP-ului brokerului. | ||
| + | - Ceasul ESP32 este aproximativ corect (certificatele au dată de expirare). | ||
| + | - Ca test temporar, puteți folosi ''espClient.setInsecure()'' pentru a dezactiva validarea. | ||
| + | </note> | ||
| + | |||
| + | ===== 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. | ||
| + | |||
| + | <code bash> | ||
| + | pip install bandit | ||
| + | bandit -r receiver/ | ||
| + | </code> | ||
| + | |||
| + | **Ce caută Bandit?** | ||
| + | |||
| + | * **Hardcoded secrets**: Parole sau chei scrise direct în cod. | ||
| + | * **Weak cryptography**: Algoritmi învechiți (MD5, SHA1 pentru hashing de securitate). | ||
| + | * **Subprocess injection**: Apeluri ''os.system()'' sau ''subprocess'' cu input nesanitizat. | ||
| + | |||
| + | ==== 2.2 Cppcheck (C/C++) ==== | ||
| + | |||
| + | Analizează codul firmware-ului pentru bug-uri și vulnerabilități. | ||
| + | |||
| + | <code bash> | ||
| + | sudo apt-get install cppcheck | ||
| + | cppcheck --enable=all --inconclusive --std=c++11 camera/src/ | ||
| + | </code> | ||
| + | |||
| + | **Ce caută Cppcheck?** | ||
| + | |||
| + | * **Buffer overflows**: Scrierea în afara limitelor unui array (critic pe dispozitive embedded!). | ||
| + | * **Uninitialized variables**: Comportament nedefinit care poate cauza crash-uri aleatorii. | ||
| + | * **Memory leaks**: Pierderi de memorie care duc la blocarea dispozitivului în timp. | ||
| + | * **Null pointer dereference**: Accesarea unui pointer nul. | ||
| + | |||
| + | ===== 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). | ||
| + | * Dacă un atacator copiază flash-ul fizic, va vedea doar date criptate. | ||
| + | * Dispozitivul decriptează transparent codul la boot. | ||
| + | |||
| + | ==== 3.2 Secure Boot ==== | ||
| + | |||
| + | Verifică **semnătura digitală** a firmware-ului la fiecare pornire. | ||
| + | * Dezvoltatorul semnează firmware-ul cu o cheie privată. | ||
| + | * Bootloader-ul verifică semnătura — dacă nu se potrivește (firmware modificat), dispozitivul **refuză să pornească**. | ||
| + | * Previne instalarea de firmware malițios. | ||
| - | ===== Funcționalități ===== | + | ==== 3.3 Verificarea stării cu ''espefuse.py'' ==== |
| - | ==== 1. Configurarea instrumentelor de analiză statică ==== | + | Putem inspecta starea de securitate a unui cip **fără a modifica nimic**: |
| - | * Instalarea și configurarea instrumentelor specifice limbajului utilizat în aplicație | + | |
| - | * Definirea regulilor de analiză bazate pe standarde de securitate și bune practici | + | |
| - | * Rularea manuală a analizei și interpretarea rezultatelor | + | |
| - | ==== 2. Detectarea și remedierea vulnerabilităților ==== | + | <code bash> |
| - | * Identificarea problemelor raportate de analiză (SQL injection, XSS, utilizare de dependențe nesigure etc.) | + | espefuse.py --port /dev/ttyACM0 summary |
| - | * Revizuirea codului și aplicarea corecțiilor necesare | + | </code> |
| - | * Compararea rezultatelor înainte și după remediere | + | |
| - | ==== 3. Integrarea analizei în pipeline-ul CI/CD ==== | + | Urmăriți secțiunea **Security fuses**: |
| - | * Automatizarea rulării analizei statice la fiecare commit/pull request | + | * ''FLASH_CRYPT_CNT'': Contor criptare flash (0 = dezactivat). |
| - | * Configurarea unor praguri pentru acceptarea sau respingerea codului nou | + | * ''ABS_DONE_0'': Secure Boot (0 = dezactivat, 1 = activat). |
| - | * Generarea și publicarea rapoartelor detaliate | + | |
| - | ===== Evaluare ===== | + | <note warning> |
| - | * Configurarea corectă a instrumentelor de analiză statică (30%) | + | **PERICOL DE BRICK-UIRE PERMANENTĂ A PLĂCUȚEI DE LABORATOR!** |
| - | * Detectarea și remedierea vulnerabilităților identificate (40%) | + | |
| - | * Integrarea analizei statice în pipeline-ul CI/CD (30%) | + | |
| - | ===== Resurse suplimentare ===== | + | 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)**. |
| - | * [https://owasp.org/www-project-top-ten/ OWASP Top 10] / [https://cwe.mitre.org CWE Database] | + | |
| - | * [https://docs.sonarqube.org/latest/ SonarQube Documentation] / [https://pypi.org/project/bandit/ Bandit (Python)] | + | |
| - | * [https://eslint.org/docs/latest/ ESLint (JavaScript)] / [https://spotbugs.github.io SpotBugs (Java)] | + | |
| - | * [https://sarifweb.azurewebsites.net SARIF Specification] | + | |
| - | * [[https://wiki.sei.cmu.edu/confluence/display/android/Android+Secure+Coding+Standard|Android Secure Coding Standard]] | + | |
| + | **Reguli stricte:** | ||
| + | - Folosiți **NUMAI** comanda ''summary'' (doar citire). | ||
| + | - **NU** rulați **NICIO** altă comandă `espefuse.py` fără aprobarea asistentului. | ||
| + | - 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. | ||
| + | - Studenții care distrug plăcuțe prin neglijență vor fi responsabili de înlocuirea lor. | ||
| + | </note> | ||
| + | ===== Exerciții ===== | ||
| + | - **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). | ||
| + | - **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''. | ||
| + | - **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". | ||