Wprowadzenie
CWE-190 to identyfikator w Common Weakness Enumeration (CWE), katalogu słabych punktów i luk bezpieczeństwa oprogramowania. Kategoria ta opisuje błędy wynikające z nieprawidłowej obsługi operacji arytmetycznych na liczbach całkowitych, prowadzące do ich przepełnienia (overflow) lub niedomiarowania (underflow). Takie błędy, choć często subtelne, mogą mieć katastrofalne skutki dla bezpieczeństwa i stabilności aplikacji, w tym tych wykorzystujących sztuczną inteligencję, poprzez naruszenie integralności danych, kontroli przepływu programu czy nawet umożliwienie zdalnego wykonania kodu.
Jak działają przepełnienia i niedomiarowania liczb całkowitych?
Problem przepełnienia i niedomiarowania liczb całkowitych występuje, gdy wynik operacji arytmetycznej na liczbach całkowitych przekracza maksymalną lub minimalną wartość, którą dany typ danych może przechowywać. W większości języków programowania (np. C, C++) liczby całkowite mają zdefiniowany zakres wartości. Gdy wynik operacji wykracza poza ten zakres, liczba „zawija się” (wraps around) do przeciwnego końca zakresu. Przykładowo, dodanie 1 do maksymalnej wartości 16-bitowej liczby całkowitej bez znaku (65535) spowoduje, że wynik będzie wynosił 0 zamiast 65536. Podobnie, odjęcie 1 od minimalnej wartości (0) da maksymalną wartość. To nieoczekiwane zachowanie może prowadzić do poważnych problemów bezpieczeństwa. Konsekwencje przepełnienia i niedomiarowania liczb całkowitych mogą być różnorodne. Błąd może spowodować, że alokowana pamięć będzie miała nieprawidłowy rozmiar (np. zbyt mały bufor, prowadzący do przepełnienia bufora - CWE-120), pętle będą wykonywać się niepoprawną liczbę razy, indeksy tablic będą poza zakresem, lub wartości krytyczne dla logiki aplikacji (np. finansowe, kryptograficzne) zostaną zniekształcone. Złośliwy aktor może celowo manipulować danymi wejściowymi, aby wywołać takie zawinięcie i wykorzystać powstałą lukę.
Główne zalety i charakterystyka
Chociaż CWE-190 nie jest "zaletą" w tradycyjnym sensie, zrozumienie jego charakterystyki jest kluczowe dla inżynierów bezpieczeństwa. Jest to jedna z podstawowych luk, często niezauważana, a jednak będąca przyczyną wielu poważnych problemów bezpieczeństwa. Może dotyczyć wielu różnych operacji: obliczania rozmiaru pamięci, licznika pętli, indeksowania tablic, arytmetyki finansowej czy kryptograficznej. Luki te mogą być trudne do zidentyfikowania podczas testów funkcjonalnych, ponieważ system może działać pozornie poprawnie dla większości typowych danych wejściowych, a błąd ujawnia się tylko w ekstremalnych przypadkach. Wykorzystanie przepełnienia i niedomiarowania liczb całkowitych może prowadzić do szeregu problemów, od odmowy usługi (DoS), przez wyciek informacji, aż po zdalne wykonanie kodu i eskalację uprawnień.
Zastosowania w praktyce
- Obliczanie rozmiaru buforów, prowadzące do alokacji zbyt małej pamięci i późniejszego przepełnienia bufora.
- Kontrola pętli, gdzie licznik może się zawinąć, powodując nieskończoną pętlę lub pominięcie krytycznych iteracji.
- Indeksowanie tablic lub struktur danych, gdzie obliczony indeks staje się ujemny lub przekracza maksymalny rozmiar, prowadząc do odczytu/zapisu poza dozwolonym obszarem pamięci.
- Operacje finansowe lub kryptograficzne, gdzie zniekształcone wartości mogą prowadzić do nieprawidłowych transakcji, złamania szyfrowania lub nieautoryzowanego dostępu.
- Systemy autoryzacji i kontroli dostępu, gdzie manipulacja wartościami liczbowymi (np. ID użytkownika, poziom uprawnień) może prowadzić do eskalacji uprawnień.
Porównanie z innymi strukturami danych
CWE-190, jako problem arytmetyczny, często bywa prekursorem innych luk bezpieczeństwa. Jest to istotna różnica w porównaniu do: * **CWE-120 (Buffer Copy without Checking Size of Input):** Przepełnienie bufora jest luką w zarządzaniu pamięcią, polegającą na zapisie danych poza granice przydzielonego bufora. Choć odmienne w swej naturze, CWE-190 (np. błędne obliczenie rozmiaru bufora) jest częstą przyczyną powstania CWE-120. * **CWE-134 (Uncontrolled Format String):** Luka format string polega na niezaufanych danych wejściowych używanych jako argumenty dla funkcji formatujących (np. `printf`), co może prowadzić do odczytu/zapisu dowolnej pamięci lub wykonania kodu. Różni się od CWE-190 tym, że nie dotyczy operacji arytmetycznych, a manipulacji formatowaniem danych wyjściowych. * **CWE-192 (Integer Coercion Error) i CWE-193 (Truncation of Slicing of Integer Value):** Są to powiązane, ale odrębne kategorie. CWE-192 dotyczy błędów w konwersji typów, a CWE-193 utraty danych podczas obcinania wartości całkowitej do mniejszego typu. O ile wszystkie te błędy dotyczą nieprawidłowej obsługi liczb, CWE-190 skupia się na przekroczeniu zakresu wartości podczas operacji arytmetycznych.
Najlepsze praktyki (2026)
- Weryfikacja i walidacja danych wejściowych: Zawsze należy weryfikować, czy dane wejściowe mieszczą się w oczekiwanym i bezpiecznym zakresie przed użyciem ich w operacjach arytmetycznych.
- Używanie odpowiednich typów danych: Wybieranie typów całkowitych o wystarczającym rozmiarze, aby pomieścić wszystkie potencjalne wartości wyników, zwłaszcza dla zmiennych kontrolujących rozmiary pamięci lub indeksy.
- Sprawdzanie wyników operacji arytmetycznych: Implementacja jawnych kontroli przed i po operacjach arytmetycznych, aby upewnić się, że wyniki nie przekraczają granic typów danych. Wiele języków oferuje funkcje do bezpiecznych operacji arytmetycznych.
- Wykorzystanie bezpiecznych bibliotek i funkcji: Używanie bibliotek, które automatycznie obsługują przepełnienia (np. przez zgłaszanie wyjątków, rozszerzanie typu) lub języków programowania, które zapewniają wbudowane mechanizmy ochrony.
- Statyczna i dynamiczna analiza kodu: Regularne przeprowadzanie analizy kodu (SAST i DAST) w celu automatycznego wykrywania potencjalnych miejsc, gdzie mogą wystąpić przepełnienia i niedomiarowania liczb całkowitych.
Typowe błędy i pułapki
- Nieweryfikowanie danych wejściowych: Zakładanie, że dane wejściowe od użytkownika lub z niezaufanego źródła zawsze będą mieścić się w bezpiecznym zakresie.
- Brak kontroli po operacjach: Nieweryfikowanie, czy wynik operacji arytmetycznej nie spowodował przepełnienia lub niedomiarowania, szczególnie gdy wynik jest używany jako rozmiar, indeks lub licznik.
- Użycie zbyt małych typów danych: Wybieranie typów danych (np. `short` lub `int`), które nie są wystarczająco duże, aby pomieścić wszystkie możliwe wartości, zwłaszcza w systemach 64-bitowych, gdzie operacje mogą zwracać bardzo duże liczby.
- Niezrozumienie specyfiki języka: Nieznajomość, jak dany język programowania obsługuje przepełnienia i niedomiarowania liczb całkowitych (np. czy "zawija" wartości, czy zgłasza błąd).