Istnieje wiele zagrożeń bezpieczeństwa związanych z bliskim kontaktem ze sprzętem, w przeciwieństwie do używania dobrze przetestowanych i sprawdzonych interfejsów API z języków programowania wysokiego poziomu. O wiele łatwiej jest spowodować przepełnienie bufora w C niż w języku takim jak Java.
Jakie są zagrożenia lub słabości (np. Przepełnienie bufora), o których powinien wiedzieć każdy programista C (luki IE istotne dla programistów C)? Do jakich problemów może to prowadzić? Jak ich uniknąć i jakie są typowe błędy powodujące ich występowanie w programach?
Odpowiedzi:
Przepełnienia bufora są duże. Domyślnie nic w C nie jest sprawdzane, więc bardzo łatwo jest nadpisać bufor. Istnieje standardowa funkcja biblioteki
gets()
, której nie można zatrzymać przed przepełnieniem bufora i prawie nigdy nie powinna być używana.Istnieją pewne techniki na poziomie implementacji, które utrudniają wykorzystanie, takie jak szyfrowanie bloków sterty, ale to nie powstrzyma przepełnienia bufora w lokalnych buforach, które często mogą robić interesujące rzeczy, takie jak zmiana adresu, do którego funkcja wróci.
Nie ma dobrego ogólnego rozwiązania w C. Wiele funkcji bibliotecznych ma wersje, które ograniczą ilość, którą będą pisać. chociaż obliczanie tego może być niezdarne. Istnieje oprogramowanie, które może wykryć przepełnienie bufora sterty w teście, o ile przeprowadzany jest odpowiedni test, a przepełnienie stosu często pojawia się jako awaria podczas testowania. Poza tym jest to kwestia starannego kodowania i przeglądu kodu.
Pokrewnym problemem jest problem zapisywania do bufora zbyt małego o jeden znak, zapominając, że ze względu na
'\0'
terminator łańcuch C o długości n znaków wymaga n + 1 znaków w pamięci . Jeśli atakującemu uda się zapisać ciąg bez terminatora, dowolna funkcja C oczekująca, że łańcuch będzie kontynuował przetwarzanie, aż osiągnie zero bajtów, co może spowodować skopiowanie lub wysłanie większej ilości informacji niż jest to pożądane (lub uderzenie w chronioną pamięć dla ataku DOS ). Rozwiązaniem są ponownie: świadomość, opieka i recenzje kodu.Z
printf()
rodziną jest jeszcze inne ryzyko . Jeśli kiedykolwiek piszeszchar * str; ... printf(str);
, ustawiasz się na problemy, jeślistr
zawiera „%” po wydrukowaniu.%n
Dyrektywa format pozwalaprintf()
na zapis do pamięci. Rozwiązaniem jestprintf("%s", str);
lubputs(str);
. (snprintf()
Zamiast tego użyj C99 zamiastsprintf()
.)Używanie liczb całkowitych bez znaku, szczególnie jako indeksów pętli, może powodować problemy. Jeśli przypiszesz małą wartość ujemną do niepodpisanego, otrzymasz dużą wartość dodatnią. Może to podważyć rzeczy takie jak przetwarzanie tylko N wystąpień czegoś lub ograniczone funkcje, takie jak
strncpy()
. Sprawdź wszystkie liczby całkowite bez znaku. Możesz tego uniknąćunsigned short
, ponieważ duża wartość jednej z nich zamieni się w dużą wartość dodatnią wint
.Nie zapominaj, że stała postaci w C jest w rzeczywistości an
int
. Pisanie czegoś podobnegochar c; while((c = getchar()) != EOF) ...
może łatwo zakończyć się niepowodzeniem, ponieważEOF
nie będzie reprezentowalne wchar
.Są o wiele bardziej charakterystyczne błędy C, o których mogę myśleć, ale mogą powodować problemy z bezpieczeństwem.
źródło
printf("%s", str)
nagiego sznurka, gdyputs(str)
wykona tę samą pracę.puts
dodaje znak nowej linii, podczas gdyprintf
nie.fputs(str, stdout)
, co nie.Niektóre z ryzyk specyficznych dla C obejmują: przepełnienie bufora , formatowanie ataków łańcuchowych i przepełnienie liczb całkowitych .
źródło
Łatwo przeoczyć ryzyko, które może powodować problemy, których naprawienie zajmie wiele godzin.
Rozważ następujący kod, który skompiluje się bez żadnych problemów.
Kiedy sprawdzisz, czy
lpstr_current_state
jest wCONST_EMERGENCY_STATE_HOLY_CRAP
środku, faktycznie przypisujesz. Lepiej zawsze umieszczać zmienną stałą po lewej stronie. Po umieszczeniu stałej po lewej stronie kompilator zawiedzie, ponieważ nie można przypisać wartości do zmiennej.Następnie możesz łatwo powiedzieć sobie: „Kurcze, to mogło być złe”, jednocześnie poprawiając kod do odczytu ...
źródło
=
i==
.Istnieje tylko jedno zagrożenie bezpieczeństwa: fakt, że na zewnątrz są ludzie, którzy dołożą wszelkich starań, aby złapać lukę w zabezpieczeniach oprogramowania i wykorzystać je dla własnych korzyści. Wszystko inne wynika stąd.
Kiedy więc myślisz, że „nikt przy zdrowych zmysłach nie zrobiłby ...”, musisz natychmiast pomyśleć „tylko ktoś, kto chce włamać się do komputerów innych ludzi, zrobiłby dokładnie to”.
Największą konsekwencją jest to, że za każdym razem, gdy reagujesz na zdarzenia na zewnątrz (na przykład przetwarzając dane dostarczane z zewnątrz), musisz założyć, że dane te były pod kontrolą twojego najgorszego wroga.
źródło