Rosnąca liczba incydentów związanych z atakami na systemy embedded sprawia jednak, że cyberbezpieczeństwo w systemach mikroprocesorowych staje się tematem kluczowym. Urządzenia te pracują często w środowiskach o ograniczonych zasobach, bez dostępu do klasycznych rozwiązań znanych z komputerów PC czy serwerów, takich jak rozbudowane systemy antywirusowe, firewalle czy złożone systemy aktualizacji. W rezultacie zabezpieczenia muszą być integrowane już na poziomie sprzętu i oprogramowania układowego, a ich skuteczność zależy od prawidłowego wdrożenia w całym cyklu życia urządzenia – od produkcji, poprzez eksploatację, aż po wycofanie z rynku.
Specyficzne rodzaje zagrożeń oraz ograniczenia sprzętowe
Systemy mikroprocesorowe charakteryzują się istotnymi ograniczeniami sprzętowymi, które istotnie wpływają na możliwości implementacji mechanizmów bezpieczeństwa. Typowy mikrokontroler dysponuje pamięcią RAM nieprzekraczającą kilkuset kilobajtów, pamięcią nieulotną Flash dochodzącą do kilku megabajtów, zaś jego częstotliwość pracy może dochodzić co najwyżej do kilkuset megaherców. W takich warunkach implementacja rozbudowanych algorytmów bezpieczeństwa, znanych z systemów klasy PC, jest niemożliwa, dlatego konieczne jest stosowanie mechanizmów ochronnych, które charakteryzują się mniejszym zapotrzebowaniem na moc obliczeniową oraz zużycie pamięci. Mechanizmy te są z reguły silnie powiązane z rozwiązaniami sprzętowymi, co pozwala odciążyć układ procesora.
Katalog zagrożeń, którym musi stawić czoło system mikroprocesorowy, obejmuje dwa główne wektory ataku: ataki logiczne/zdalne oraz ataki fizyczne.
Ataki zdalne wynikają z faktu, że mikrokontrolery często obsługują interfejsy komunikacyjne narażone na ingerencję zewnętrzną. Przykładem są stosy TCP/IP w urządzeniach IoT, transmisja bezprzewodowa (Wi-Fi, Bluetooth Low Energy, ZigBee, LoRaWAN) oraz przewodowa (UART, CAN, Modbus). Wektory zagrożeń obejmują m.in. ataki typu man-in-the-middle na poziomie protokołu, wstrzykiwanie złośliwych pakietów, eskalację uprawnień w oprogramowaniu systemowym oraz nieautoryzowane aktualizacje oprogramowania. Szczególnie krytyczne konsekwencje mogą mieć błędy w oprogramowaniu bootloadera, które mogą pozwolić na obejście procesu weryfikacji integralności kodu.
Ataki fizyczne obejmują manipulację sprzętową i analizę pracy układu. Najprostszą metodą jest wykorzystanie interfejsów debugowania (JTAG, SWD) do odczytu pamięci programu lub rejestrów wewnętrznych. W bardziej zaawansowanych scenariuszach stosuje się atak kanałem bocznym, w którym analiza poboru prądu lub emisji elektromagnetycznej pozwala na ekstrakcję przechowywanych w urządzeniu kluczy kryptograficznych. Kolejną kategorią są ataki polegające na tzw. wstrzykiwaniu błędów (fault injection), realizowane poprzez celowe zaburzenia napięcia zasilania (voltage glitching), manipulacje zegarem (clock glitching) lub ekspozycję układu na impulsy laserowe. Wstrzyknięcie błędu w odpowiednim stanie układu może prowadzić np. do pominięcia instrukcji weryfikacji integralności kodu.
Szczególnie krytycznym momentem w cyklu życia urządzenia jest faza bootowania. To na tym etapie następuje inicjalizacja sprzętu i załadowanie pierwszego fragmentu kodu z pamięci nieulotnej. Brak weryfikacji integralności i autentyczności kodu programu umożliwia trwałą infekcję urządzenia poprzez zapis złośliwego kodu do pamięci Flash. W praktyce prowadzi to do przejęcia pełnej kontroli nad systemem, ponieważ złośliwe oprogramowanie może uruchamiać się przy każdym restarcie i funkcjonować równolegle lub zamiast legalnej aplikacji.
Drugim krytycznym zagadnieniem jest ochrona materiału kryptograficznego. Klucze prywatne przechowywane w niechronionej pamięci Flash są szczególnie podatne na zdobycie w wyniku ataku fizycznego. Ich kompromitacja pozwala na złamanie całego łańcucha zaufania – zarówno w kontekście komunikacji szyfrowanej, jak i procesu aktualizacji urządzenia. Dlatego we współczesnych układach coraz częściej znaleźć można zintegrowane dedykowane bloki sprzętowe, pozwalające na bezpieczne przechowywanie oraz generowanie kluczy kryptograficznych.
Specyfika systemów mikroprocesorowych sprawia, że ich bezpieczeństwo musi być traktowane jako efekt współdziałania architektury sprzętowej oraz oprogramowania. Brak odpowiednich zabezpieczeń na poziomie fizycznym i logicznym prowadzi do sytuacji, w której każde przełamanie pojedynczej warstwy ochrony może skutkować kompromitacją całego projektu.
Fundamenty bezpieczeństwa – ładowanie kodu programu
Podstawą każdego bezpiecznego systemu mikroprocesorowego jest zagwarantowanie, że uruchamiany kod pochodzi z zaufanego źródła i nie został zmodyfikowany w nieautoryzowany sposób. Koncepcja ta określana jest mianem Root of Trust (RoT) i stanowi fundament całego łańcucha zabezpieczeń systemu.
Root of Trust to podstawowa warstwa bezpieczeństwa, która działa jak swego rodzaju sejf – przechowuje dane o krytycznym znaczeniu z punktu widzenia poufności oraz integralności systemu, do których należeć mogą np. klucze kryptograficzne oraz kod programu związany z procedurami uruchamiania urządzenia, autentykacji oraz weryfikacji pozostałej części aplikacji. W systemach embedded RoT implementowany jest z reguły w postaci sprzętowej jako obszar pamięci ROM niemożliwy do modyfikacji po zakończeniu procesu produkcyjnego. Dzięki temu atakujący, nawet mając fizyczny dostęp do urządzenia, nie jest w stanie zmienić kodu stanowiącego podstawę łańcucha zaufania.
Kluczowym mechanizmem realizowanym przez RoT jest bezpieczny boot (secure boot). Polega on na weryfikacji integralności i autentyczności kolejno uruchamianych warstw oprogramowania. Proces ten zwykle realizowany jest poprzez obliczenie funkcji skrótu z umieszczonego w pamięci kodu programu, a następnie porównanie go z podpisem cyfrowym zweryfikowanym przy użyciu klucza publicznego również przechowywanego w RoT.
W praktyce secure boot tworzy łańcuch zaufania (chain of trust). Każdy kolejny etap (np. bootloader drugiego stopnia, system operacyjny czasu rzeczywistego, aplikacja) weryfikowany jest przez etap wcześniejszy. Jeśli w którymkolwiek momencie weryfikacja zakończy się niepowodzeniem, uruchamianie zostaje przerwane, a system przechodzi w tryb awaryjny.
W nowoczesnych mikrokontrolerach stosuje się dodatkowe mechanizmy zwiększające odporność procesu bootowania:
- klucze sprzętowe generowane indywidualnie dla każdego układu podczas produkcji,
- sprzętowe akceleratory kryptograficzne przyspieszające proces autentykacji i kontroli integralności,
- mechanizmy uniemożliwiające cofnięcie się do starszej, podatnej wersji oprogramowania.
Warto podkreślić, że skuteczność secure boot w dużej mierze zależy od jakości implementacji. Błędy w kodzie bootloadera, niewłaściwe zarządzanie kluczami czy brak odpowiednich zabezpieczeń fizycznych mogą prowadzić do obejścia całego mechanizmu. Z tego powodu proces projektowania RoT powinien być traktowany jako element krytyczny, uwzględniający zarówno architekturę sprzętową układu, jak i wymagania środowiska, w którym system będzie pracować.
Mechanizmy izolacji oraz ochrona pamięci
Kolejnym fundamentem cyberbezpieczeństwa w systemach mikroprocesorowych jest zapewnienie izolacji pomiędzy obszarami kodu o różnym poziomie zaufania oraz ochrona krytycznych zasobów pamięci. W praktyce oznacza to stworzenie środowiska, w którym błędy obecne w jednym z fragmentów aplikacji nie mogą zostać łatwo wykorzystane do przejęcia pełnej kontroli nad pracą całego systemu.
Mechanizmy izolacji i ochrony pamięci są kluczowe dla minimalizacji skutków udanego ataku. Nawet jeśli napastnikowi uda się wykorzystać podatność w aplikacji użytkownika, to odpowiednio skonfigurowana architektura systemu uniemożliwi mu dostęp do zasobów krytycznych – bootloadera, kluczy kryptograficznych czy mechanizmów bezpieczeństwa umieszczonych w obszarze RoT.
W nowoczesnych mikrokontrolerach architektura sprzętowa coraz częściej wspiera izolację kodu i danych. Przykładem jest technologia ARM TrustZone dla Cortex-M, która wprowadza koncepcję dwóch oddzielnych środowisk: bezpiecznego (secure world) oraz o niskim poziomie zaufania (non-secure world). Mechanizm ten umożliwia podział pamięci programu i danych na regiony dostępne wyłącznie dla oprogramowania zaufanego oraz te przeznaczone dla aplikacji użytkownika. Przełączanie kontekstu pomiędzy obszarami realizowane jest przez specjalne instrukcje i kontrolowane sprzętowo, co minimalizuje ryzyko uzyskania nieuprawnionego dostępu.
W systemach bardziej złożonych stosuje się także rozwiązania oparte na architekturze wielordzeniowej, gdzie jeden z rdzeni realizuje funkcje związane z bezpieczeństwem, a pozostałe obsługują aplikację. W takiej konfiguracji izolacja także realizowana jest fizycznie, poprzez dedykowany układ sprzętowy.
Drugim filarem bezpieczeństwa jest kontrola dostępu do pamięci programu i danych. W mikrokontrolerach realizowana jest ona najczęściej za pomocą:
- Memory Protection Unit (MPU) – jednostki pozwalającej na definiowanie regionów pamięci o różnym poziomie uprawnień. Dzięki temu aplikacja użytkownika nie ma możliwości nadpisania fragmentów pamięci zawierających kod systemowy lub klucze kryptograficzne.
- Execute-Only Memory (XOM) – obszar pamięci, który umożliwia wykonywanie umieszczonych w nim instrukcji przez procesor, nie pozwala jednak na odczyt zawartości tej pamięci przez inne fragmenty aplikacji.
- Sprzętowego układu typu firewall – rozwiązanie sprzętowe stosowane m.in. w niektórych procesorach z rodziny STM32, umożliwiające definiowanie obszarów pamięci dostępnych wyłącznie dla kodu wykonywanego w trybie chronionym.
W celu dodatkowej ochrony coraz częściej stosuje się również mechanizmy detekcji manipulacji. Polegają one na monitorowaniu integralności pamięci i generowaniu przerwań w przypadku próby nieautoryzowanego dostępu lub zmiany konfiguracji poziomu ochrony.
Zabezpieczenie kanałów debugowania
Szczególną uwagę należy zwrócić na interfejsy debugowania, takie jak JTAG i SWD. Pozostawienie ich aktywnych w urządzeniu produkcyjnym stanowi krytyczne zagrożenie, umożliwiające pełny odczyt pamięci i przejęcie kontroli nad układem. Standardową praktyką jest wyłączanie tych interfejsów za pomocą tzw. lock bits, które fizycznie dezaktywują interfejsy debugowania po zakończeniu etapu produkcji i programowania. W bardziej zaawansowanych układach stosuje się autoryzację dostępu do debuggera przy użyciu mechanizmów kryptograficznych.
Obsługa kluczy i kryptografia
Jednym z najważniejszych elementów bezpieczeństwa systemów mikroprocesorowych jest właściwe zarządzanie kluczami kryptograficznymi oraz implementacja algorytmów kryptograficznych w sposób odporny na ataki. Mikrokontroler, niezależnie od przeznaczenia, musi zapewniać nie tylko poprawną implementację kryptografii, ale przede wszystkim bezpieczne przechowywanie i używanie materiału kryptograficznego.
Prawidłowe zarządzanie kluczami i wykorzystanie sprzętowego wsparcia kryptografii to jeden z filarów bezpieczeństwa systemów mikroprocesorowych. Ujawnienie kluczy prywatnych prowadzi do całkowitego złamania łańcucha zaufania – stąd rosnące znaczenie wbudowanych modułów bezpieczeństwa, które zdejmują odpowiedzialność za krytyczne operacje kryptograficzne z aplikacji użytkownika i realizują je w wydzielonych, chronionych środowiskach sprzętowych.
Klucze kryptograficzne nie mogą być przechowywane w pamięci Flash w postaci jawnej, ponieważ są narażone na odczyt w wyniku ataku fizycznego lub poprzez sprzętowe interfejsy debugowania. W nowoczesnych mikrokontrolerach stosuje się dedykowane mechanizmy sprzętowe, takie jak:
- Układ HUK (Hardware Unique Key) – unikalny klucz generowany indywidualnie dla każdego układu w procesie produkcji. Stanowi podstawę do dalszego wyprowadzania kluczy sesyjnych.
- Pamięć typu OTP (One Time Programmable) – specjalne obszary pamięci, które mogą być zapisane tylko jednokrotnie, w dodatku często nie jest możliwy ich odczyt z poziomu aplikacji użytkownika.
- Układy Secure Key Storage / Key Wrapping – mechanizmy umożliwiające przechowywanie kluczy w postaci zaszyfrowanej i ich odszyfrowywanie dopiero wewnątrz bezpiecznego modułu kryptograficznego.
Ze względu na ograniczoną moc obliczeniową mikroprocesorów większość współczesnych układów tego typu ma wbudowane akceleratory kryptograficzne. Umożliwiają one realizację operacji szyfrowania i podpisu cyfrowego przy znacznie niższym zużyciu energii oraz zwiększonej odporności na ataki kanałami bocznymi.
Do typowych zadań realizowanych przez sprzętowe układy kryptograficzne zaliczyć można realizację algorytmów szyfrowania symetrycznego (AES) oraz asymetrycznego (RSA, ECC, ECDSA), wyliczanie funkcji skrótu oraz generowanie liczb losowych.
Szyfrowanie kodu programu
Jeśli do przechowywania kodu programu wykorzystywany jest zewnętrzny układ pamięci (umożliwiający np. komunikację po SPI lub I2C), istnieje bardzo duże zagrożenie związane z nieszyfrowaną transmisją kodu programu poprzez otwarty kanał komunikacyjny. Rozwiązaniem tego problemu może być technika określana jako Decryption On-The-Fly (DOTF). Polega ona na przechowywaniu kodu programu w pamięci w postaci zaszyfrowanej, a następnie deszyfrowaniu go dopiero w momencie wykonywania przez CPU. Rozwiązanie to chroni zarówno własność intelektualną producenta systemu, jak i utrudnia napastnikowi realizację ataków polegających na wstrzyknięciu złośliwego kodu.
Bezpieczna aktualizacja oprogramowania
Aktualizacja oprogramowania jest nieodzownym elementem cyklu życia współczesnych systemów mikroprocesorowych. Umożliwia usuwanie wykrytych podatności, wprowadzanie nowych funkcji oraz zapewnienie zgodności z aktualnymi standardami komunikacyjnymi. W kontekście cyberbezpieczeństwa największym wyzwaniem jest zagwarantowanie, że proces aktualizacji zostanie przeprowadzony w sposób odporny na ataki i nie doprowadzi do zainstalowania złośliwego oprogramowania.
Z aktualizacją oprogramowania systemu wiąże się szereg zagrożeń. Do najpowszechniej stosowanych rodzajów ataków zaliczyć można:
- przechwycenie komunikacji i podmiana obrazu firmware (tzw. atak typu man in the middle),
- ponowne wprowadzenie starszej, podatnej wersji oprogramowania w celu obejścia poprawek,
- modyfikację fragmentu obrazu aktualizacji, prowadzącą do wstrzyknięcia złośliwego fragmentu kodu,
- próbę ominięcia mechanizmów weryfikacji podpisu cyfrowego w trakcie instalacji nowej wersji oprogramowania.
Aby zabezpieczyć proces aktualizacji, stosuje się szereg technik i rozwiązań. Do najpopularniejszych zaliczyć można:
- Szyfrowanie transmisji, co zapobiega przechwyceniu i analizie przesyłanych danych.
- Weryfikację autentyczności oprogramowania za pomocą podpisu cyfrowego – obraz jest podpisywany przez producenta za pomocą klucza prywatnego, następnie zaś weryfikowany lokalnie przez mikrokontroler na podstawie posiadanego przez niego klucza publicznego.
- Ochronę przed downgrade’em – urządzenie przechowuje numer wersji aktualnie zainstalowanego oprogramowania i nie akceptuje niższych wersji, eliminując ryzyko przywrócenia wykrytych i załatanych już podatności.
- Architekturę dual bank – oprogramowanie przechowywane jest w dwóch niezależnych segmentach pamięci Flash, co pozwala na instalację nowej wersji równolegle z działającą i umożliwia powrót do poprzedniej wersji w przypadku błędu podczas aktualizacji.
Proces aktualizacji musi być możliwie odporny na przerwania zasilania i zaniki komunikacji. W tym celu stosuje się mechanizmy etapowej aktualizacji, w których nowa aplikacja jest instalowana dopiero po pełnym pobraniu i zweryfikowaniu jej integralności i autentyczności.
W bardziej zaawansowanych implementacjach stosuje się ponadto dodatkowe warstwy ochrony, takie jak rozdzielenie kluczy odpowiedzialnych za różne etapy aktualizacji, hierarchię certyfikatów oraz możliwość odwołania skompromitowanych kluczy.
Wyzwania i ograniczenia techniczne
Pomimo dostępności zaawansowanych mechanizmów bezpieczeństwa, ich implementacja w systemach mikroprocesorowych bardzo często napotyka szereg ograniczeń natury sprzętowej, programowej oraz organizacyjnej.
Mikrokontrolery charakteryzują się stosunkowo niewielką ilością pamięci RAM oraz ograniczoną mocą obliczeniową. Wprowadzenie złożonych algorytmów kryptograficznych czy mechanizmów wirtualizacji wymaga często kompromisu między poziomem bezpieczeństwa a wydajnością systemu. Dla wielu aplikacji IoT priorytetem jest niskie zużycie energii, co dodatkowo ogranicza możliwość stosowania obliczeniowo kosztownych mechanizmów bezpieczeństwa.
Dodanie sprzętowych modułów bezpieczeństwa zwiększa ponadto koszt układu oraz złożoność procesu produkcyjnego. W przypadku projektów, gdzie kluczowym parametrem jest cena jednostkowa urządzenia, producenci często świadomie rezygnują z części mechanizmów zabezpieczeń.
Dodatkowo w wielu sektorach wykorzystywane są systemy embedded oparte na starszych mikrokontrolerach, nie mających dedykowanych modułów bezpieczeństwa. Integracja nowych mechanizmów ochronnych z takim środowiskiem bywa trudna lub niemożliwa, zaś zastąpienie starych urządzeń nowymi może być niewykonalne np. ze względów ekonomicznych lub technologicznych. Sytuacja taka powoduje, że w infrastrukturze systemu obecne są słabo chronione elementy, które mogą stać się punktem wejścia dla atakującego.
Podsumowanie
Cyberbezpieczeństwo w systemach mikroprocesorowych to obecnie jeden z kluczowych obszarów projektowania urządzeń embedded. Wraz z rosnącą rolą IoT, automatyki przemysłowej i systemów krytycznych mikrokontrolery stają się celem coraz bardziej wyrafinowanych ataków – zarówno logicznych, jak i fizycznych. Skuteczna ochrona wymaga wielowarstwowego podejścia, łączącego mechanizmy sprzętowe, odpowiednią konfigurację oprogramowania oraz procesy produkcyjne o wysokim poziomie kontroli. Z drugiej strony projektowanie bezpiecznych systemów wymaga często kompromisu pomiędzy kosztami, wydajnością i trwałością rozwiązania. W praktyce oznacza to, że cyberbezpieczeństwo musi być integralnym elementem procesu projektowego, a nie dodatkiem implementowanym na końcowym etapie tworzenia produktu.
Damian Tomaszewski