deserializacja niezaufanych danych (insecure deserialization)
| Po angielsku | insecure deserialization |
|---|---|
| Którą formą pisać | obie formy Obie formy są w obiegu. Przy pierwszym wystąpieniu pisz „polski termin (english term)”, dalej sam polski. |
| Inne formy | deserializacja danych od użytkownika · deserializacja danych z niezaufanych źródeł · niebezpieczna deserializacja |
| Kategoria | Podatności i klasy błędów |
Deserializacja niezaufanych danych (insecure deserialization) to podatność, w której aplikacja odtwarza obiekty z danych pochodzących z niezaufanego źródła i nie sprawdza wystarczająco, czy wynik będzie poprawny [1]. Atakujący, który kontroluje zserializowane dane, może zmienić stan aplikacji, wywołać odmowę usługi, a w skrajnym przypadku doprowadzić do wykonania kodu [1]. W katalogu CWE odpowiada jej słabość CWE-502, która w zestawieniu CWE Top 25 za 2025 rok zajęła 15. miejsce [8].
Jak działa
Serializacja zamienia obiekt w ciąg znaków albo bajtów, który da się zapisać lub przesłać; deserializacja odtwarza z niego obiekt [2]. OWASP zauważa, że formaty takie jak JSON i XML są popularne, ale natywne mechanizmy serializacji języków programowania można wykorzystać do ataku [2]. W PHP służą do tego funkcje serialize() i unserialize() [4].
Problem polega na tym, że natywny format opisuje nie tylko dane, ale też klasę obiektu i wartości jego pól. Dokumentacja PHP ostrzega, że odtworzenie obiektu może doprowadzić do załadowania i wykonania kodu przez utworzenie instancji klasy i automatyczne ładowanie klas [3]. W PHP ta odmiana nazywa się PHP Object Injection [1]; zob. PHP object injection.
// ciasteczko od przeglądarki trafia do unserialize()
$prefs = unserialize( base64_decode( $_COOKIE['prefs'] ) ); // podatne
$prefs = json_decode( base64_decode( $_COOKIE['prefs'] ), true ); // same dane
Sekurak opisuje trzy warunki ataku w PHP: deserializację danych kontrolowanych przez użytkownika, obecność w aplikacji klas z metodami magicznymi zawierającymi niebezpieczny kod oraz możliwość łączenia wywołań w łańcuch [4]. Metody magiczne, takie jak __wakeup() czy __destruct(), PHP wywołuje automatycznie, na przykład po deserializacji albo przy niszczeniu obiektu [4]. Schemat poniżej pokazuje przebieg ataku.
Odmiany
MITRE wymienia kilka nazw tego samego zjawiska, zależnie od języka i środowiska [1]. Ściąga OWASP wskazuje dla każdego języka typowe niebezpieczne funkcje [2].
| Środowisko | Nazwa lub mechanizm | Uwagi |
|---|---|---|
| ogólnie | marshaling, unmarshaling [1] | ogólne nazwy serializacji |
| PHP | PHP Object Injection, unserialize() [1] | metody magiczne [4] |
| Python | pickling, pickle.loads() [1] | także PyYAML i jsonpickle [2] |
| Java | ObjectInputStream [2] | łańcuchy gadżetów w bibliotekach [2] |
| .NET | BinaryFormatter [2] | według Microsoftu nie da się go zabezpieczyć [2] |
W WordPressie
WordPress sam przechowuje w bazie dane zserializowane. Funkcja maybe_unserialize() odtwarza dane tylko wtedy, gdy były zserializowane, i jest używana między innymi przez get_option() oraz funkcje odczytu metadanych [5]. Analiza WPScan wskazuje, że metadane przechodzą przy odczycie przez maybe_unserialize(), co przy zapisie danych od użytkownika może prowadzić do iniekcji obiektu [6]. Ryzyko pojawia się też, gdy wtyczka przekazuje do unserialize() albo maybe_unserialize() dane z żądania, ciasteczka lub innego źródła kontrolowanego przez użytkownika.
Do groźnego skutku potrzebny jest zwykle łańcuch gadżetów, czyli ciąg istniejących metod, które po połączeniu dają groźny efekt. Marc Montpas z WPScan opisał w październiku 2023 roku taki łańcuch zbudowany wyłącznie z klas rdzenia WordPressa, między innymi WP_Theme, WP_Block_List i klas biblioteki Requests [6]. WordPress 6.3.2 usunął go, blokując deserializację części klas albo ograniczając zawartość ich pól [6]. W grudniu 2023 roku WordPress 6.4.2 naprawił kolejną podatność zdalnego wykonania kodu, której według zespołu bezpieczeństwa nie dało się wykorzystać bezpośrednio w rdzeniu, ale która mogła być groźna w połączeniu z niektórymi wtyczkami, zwłaszcza w instalacjach Multisite [7]. Autorzy analizy zauważyli, że wcześniej powszechnie uważano, iż rdzeń nie zawiera działającego łańcucha prowadzącego do zdalnego wykonania kodu, więc iniekcja obiektu bez dodatkowych wtyczek wydawała się bezpieczna [6]. Dlatego aktualizacja rdzenia WordPressa ma znaczenie także wtedy, gdy podatna jest tylko wtyczka.
Zagrożenia i skutki
MITRE wymienia trzy główne skutki [1]:
- modyfikację obiektów i danych, które aplikacja uważała za niezmienne;
- odmowę usługi, na przykład przez nieskończone pętle zużywające zasoby;
- wykonanie dowolnego kodu przez łańcuchy gadżetów.
Sekurak opisuje przykład z systemu Joomla 3.0.2: podatność CVE-2013-1453, w której parametr GET deserializowany bez walidacji pozwalał usuwać katalogi na serwerze [4]. Gdy podatny punkt jest dostępny bez logowania, mamy do czynienia z podatnością wykorzystywaną bez uwierzytelnienia, a jej skutki mogą obejmować wgranie dowolnego pliku albo zapis webshella.
Jak się bronić
Ściąga OWASP wskazuje najbezpieczniejszy wzorzec: nie przyjmować zserializowanych obiektów z niezaufanych źródeł [2]. Sekurak formułuje to wprost: nigdy nie należy deserializować danych otrzymywanych od użytkownika [4].
- Format danych zamiast obiektów. Dokumentacja PHP zaleca standardowy format wymiany danych, taki jak JSON, przez
json_decode()ijson_encode()[3]. - Parametr allowed_classes nie jest ochroną. Od PHP 7.0
unserialize()przyjmuje listę dozwolonych klas, a obiekty spoza niej zamienia w__PHP_Incomplete_Class[3]. Dokumentacja zastrzega jednak, by nie przekazywać do tej funkcji niezaufanych danych niezależnie od wartości tej opcji [3]. - Podpis danych. MITRE zaleca podpis kryptograficzny, na przykład HMAC, który pozwala wykryć zmianę danych [1]; zob. funkcja skrótu (hashowanie).
- Lista dozwolonych klas. MITRE i OWASP zalecają ograniczenie deserializacji do określonych, zaufanych klas [1] [2].
Samo czyszczenie danych wejściowych nie wystarcza, bo zserializowany ciąg jest poprawny składniowo, a groźna jest jego treść.
Historia i przykłady
W zestawieniu CWE Top 25 za 2024 rok słabość CWE-502 była na 16. miejscu, a w 2025 roku awansowała o jedną pozycję [8]. MITRE wśród obserwowanych przykładów wymienia CVE-2015-4852 i CVE-2015-8103 w bibliotekach Javy oraz CVE-2012-0911, nadużycie unserialize() w systemie CMS przez spreparowane ciasteczka [1]. W WordPressie łańcuch gadżetów z klas rdzenia usunęło wydanie 6.3.2 [6], a wydanie 6.4.2 z 6 grudnia 2023 roku naprawiło podatność zdalnego wykonania kodu groźną w połączeniu z wtyczkami [7].
Przypisy
8- [1] CWE-502: Deserialization of Untrusted Data · MITRE
- [2] Deserialization Cheat Sheet · OWASP
- [3] unserialize — PHP Manual · The PHP Group
- [4] PHP Object Injection – mało znana, krytyczna klasa podatności · Sekurak
- [5] maybe_unserialize() — Code Reference · WordPress.org
- [6] Finding A RCE Gadget Chain In WordPress Core · WPScan
- [7] WordPress 6.4.2 Maintenance & Security Release · WordPress.org
- [8] 2025 CWE Top 25 Most Dangerous Software Weaknesses · MITRE