Przejdź do treści głównej
OpenAI

Dlaczego Codex Security nie zawiera raportu SAST?

Od dziesięcioleci statyczne testowanie bezpieczeństwa aplikacji (SAST) jest jednym z najskuteczniejszych sposobów, dzięki którym zespoły ds. bezpieczeństwa mogą skalować przegląd kodu. 

Jednak kiedy tworzyliśmy Codex Security, podjęliśmy świadomą decyzję projektową: nie zaczęliśmy od zaimportowania raportu z analizy statycznej i poproszenia agenta o jego wstępną klasyfikację. Zaprojektowaliśmy system tak, aby zaczynał od przeglądu samego repozytorium – jego architektury, granic zaufania i zamierzonego zachowania – i aby weryfikował wyszukane dane, zanim poprosi człowieka o ich analizę. 

Powód jest prosty: najtrudniejsze luki zwykle nie są problemami związanymi z przepływem danych. Występują, gdy kod sprawia wrażenie, że wymusza kontrolę bezpieczeństwa, ale ta kontrola w rzeczywistości nie gwarantuje właściwości, na której polega system. Innymi słowy, problem nie polega tylko na śledzeniu, jak dane przemieszczają się w programie, ale na ustaleniu, czy mechanizmy obronne w kodzie naprawdę działają.

Problem: SAST jest zoptymalizowany pod kątem przepływu danych

SAST często przedstawia się jako następujący potok: zidentyfikować źródło niezaufanych danych wejściowych, prześledzić dane w programie i oznaczyć przypadki, kiedy te dane trafiają do wrażliwego ujścia bez oczyszczenia. To elegancki model i pozwala wykryć wiele rzeczywistych błędów.

W praktyce w SAST muszą być stosowane pewne uproszczenia, aby zachować wykonalność działań na dużą skalę, zwłaszcza w rzeczywistych bazach kodu z pośrednimi odwołaniami, dynamicznym wywoływaniem metod, wywołaniami zwrotnymi, refleksją i sterowaniem przepływem opartym w dużej mierze na frameworkach. Te uproszczenia nie są zarzutem wobec SAST; to rzeczywiste sytuacje, kiedy dochodzi do wnioskowania dotyczącego kodu bez jego uruchamiania.

Jednak nie to jest powodem, dla którego Codex Security nie zaczyna od raportu SAST.

Głębszy problem polega na tym, co dzieje się po pomyślnym prześledzeniu źródła do ujścia.

Gdy analiza statyczna zawodzi: ograniczenia i semantyka

Nawet gdy analiza statyczna prawidłowo śledzi dane wejściowe w wielu funkcjach i warstwach, nadal wymagana jest odpowiedź na pytanie, które tak naprawdę decyduje o tym, czy występuje luka w zabezpieczeniach:

Czy obrona naprawdę zadziałała?

Przyjrzyjmy się powszechnie występującemu wzorcowi: kod wywołuje instrukcję sanitize_html() przed wyrenderowaniem niezaufanej treści. Analizator statyczny może zauważyć, że użyto sanityzatora. Zwykle nie potrafi natomiast ustalić, czy ten mechanizm sanityzacji jest rzeczywiście wystarczający w odniesieniu do konkretnego kontekstu renderowania, silnika szablonów, zachowania kodowania oraz związanych z tym transformacji wykonywanych dalej w łańcuchu przetwarzania.

Tu sprawy zaczynają się komplikować. Problem nie polega tylko na tym, czy dane trafiają do ujścia, ale czy kontrole w kodzie faktycznie ograniczają wartość w sposób, w jaki system zakłada, że to robią.

Innymi słowy: istnieje duża różnica między „kod wywołuje funkcję sanitizującą” a „system jest bezpieczny”.

Przykład: walidacja przed dekodowaniem

Oto wzorzec, który ciągle pojawia się w rzeczywistych systemach.

Aplikacja internetowa otrzymuje zawartość JSON, wyodrębnia redirect_url, weryfikuje go względem wyrażenia regularnego listy dozwolonych, dekoduje adres URL i przekazuje wynik do modułu obsługi przekierowań.

W klasycznym raporcie source-to-sink opis przepływu wygląda następująco:

niezaufane dane wejściowe → sprawdzenie wyrażeniem regularnym → dekodowanie adresu URL → przekierowanie

Jednak prawdziwe pytanie nie brzmi, czy taka kontrola istnieje, ale czy ta kontrola nadal ogranicza wartość po następujących transformacjach.

Jeśli to wyrażenie regularne jest uruchamiane przed dekodowaniem, czy faktycznie ogranicza zdekodowany adres URL w taki sposób, w jaki interpretuje go obsługa przekierowań?

Odpowiedź na to pytanie oznacza rozumowanie o całym łańcuchu transformacji: co dopuszcza wyrażenie regularne, jak działa dekodowanie i normalizacja, jak parsowanie adresów URL traktuje przypadki brzegowe oraz jak logika przekierowań rozwiązuje schematy i autorytety.

Składowe wielu istotnych luk są następujące: błędy w kolejności wykonywania operacji, częściowa normalizacja, niejednoznaczności parsowania oraz rozbieżności między walidacją a interpretacją. Przepływ danych jest widoczny. Słabość tkwi w tym, jak ograniczenia propagują się (lub nie propagują) w łańcuchu transformacji.

Nie jest to jednak tylko teoretyczny schemat. W luce CVE-2024-29041(otwiera nowe okno) Express był podatny na problem z otwartym przekierowanie, w ramach którego zniekształcone adresy URL mogły omijać typowe implementacje listy elementów dozwolonych ze względu na sposób, w jaki cele przekierowania były kodowane, a następnie interpretowane. Przepływ danych był prosty. Trudniejszym pytaniem i jednocześnie aspektem, który przesądzał o tym, czy błąd istniał, było to, czy walidacja nadal obowiązywała po łańcuchu transformacji.

Nasze podejście: najpierw zachowania, a potem weryfikacja

Codex Security opiera się na prostym celu: ograniczyć triaż poprzez wykrywanie problemów z mocniejszymi dowodami. W ramach produktu oznacza to korzystanie z kontekstu specyficznego dla repozytorium (co obejmuje model zagrożeń) oraz weryfikowanie problemów o wysokim sygnale w izolowanym środowisku, zanim zostaną ujawnione. 

Gdy Codex Security napotyka granicę, która wygląda jak „walidacja” lub „sanityzacja,” nie traktuje tego jak pola wyboru. Próbuje zrozumieć, jaką gwarancję kod próbuje zapewnić, a następnie próbuje podważyć tę gwarancję.

W praktyce zazwyczaj jest to połączenie:

  • Czytania odpowiedniej ścieżki kodu z pełnym kontekstem repozytorium, tak jak zrobiłby to badacz bezpieczeństwa, oraz szukania rozbieżności między intencją a implementacją. Komentarze też są uwzględniane, ale model niekoniecznie wierzy im, więc dodanie komentarza //Halvar mówi: to nie jest błąd nad kodem go nie zmyli, jeśli rzeczywiście występuje błąd.
  • Ograniczania problemu do najmniejszego testowalnego wycinka (na przykład potoku transformacji wokół jednego wejścia), aby można było go analizować bez przeszkód ze strony reszty systemu. W tym sensie Codex Security wyodrębnia drobne fragmenty kodu, a następnie pisze dla nich mikrofuzzery.
  • Rozumowania o ograniczeniach w różnych transformacjach, zamiast traktowania każdej kontroli niezależnie. W stosownych przypadkach może to obejmować sformalizowanie tego jako pytania o spełnialność. Innymi słowy, zapewniamy modelowi dostęp do środowiska Python z z3-solver, a ten dobrze sobie radzi z korzystaniem z niego, gdy jest to potrzebne, tak jak ro biłby to człowiek, reagując na wyjątkowo skomplikowany problem dot. ograniczeń wejściowych. Jest to szczególnie przydatne podczas analizy przepełnień liczb całkowitych lub podobnych błędów w niestandardowych architekturach.
  • Uruchamiania hipotez w izolowanym środowisku weryfikacyjnym (jeśli to możliwe), aby odróżnić „prawdopodobne problemy” od „braku problemów”. Nie ma lepszego dowodu niż kompletny PoC z kodem skompilowanym w trybie debugowania. 

To jest właśnie kluczowa zmiana: zamiast poprzestawać na etapie „istnieje sprawdzenie”, system dąży do stwierdzenia „inwariant jest spełniony (albo nie), a oto dowody”, a model wybiera najlepsze narzędzie do danego zadania.

Dlaczego nie zasilamy Codex Security raportem SAST?

Rozsądna reakcja to: zastosujcie oba elementy; zacznijcie od raportu SAST, a następnie użyjcie agenta, aby przeprowadzić głębsze rozumowanie.

Występują sytuacje, kiedy wstępnie obliczone wyniki są pomocne, zwłaszcza w przypadku wąskich, znanych klas błędów, jednak dla agenta zaprojektowanego do wykrywania i weryfikowania luk w zabezpieczeniach w kontekście, rozpoczęcie od raportu SAST tworzy trzy przewidywalne tryby awarii.

Po pierwsze, może to zachęcać do przedwczesnego zawężania. Lista ustaleń to mapa miejsc, które narzędzie już sprawdziło. Jeśli potraktuje się je jako punkt wyjścia, to można ukierunkować system na poświęcanie nieproporcjonalnie dużego wysiłku w tych samych obszarach, korzystanie z tych samych abstrakcji oraz pomijanie klas problemów, które nie pasują do „światopoglądu” narzędzia.

Po drugie, może to prowadzić do ukrytych osądów, które później trudno wycofać. Wiele ustaleń SAST koduje założenia dotyczące sanityzacji, walidacji lub granic zaufania. Jeśli te założenia są błędne lub niepełne, włączenie ich do pętli rozumowania może przesunąć agenta z obszaru „zbadaj” do „potwierdź lub odrzuć”, a nie takie zadanie ma wykonać agent.

Po trzecie, może to utrudnić ocenę systemu rozumowania. Jeśli potok rozpoczyna się od danych wyjściowych SAST, trudniej jest oddzielić to, co agent wykrył na podstawie własnej analizy, od tego, co przejął z innego narzędzia. Taki oddzielnie jest ważne, jeśli chcemy dokładnie mierzyć możliwości systemu, co jest niezbędne do dalszego ulepszania systemu.

Dlatego stworzyliśmy Codex Security – aby skupić się na źródle badań nad bezpieczeństwem: kodzie i intencjach systemu, gdzie walidacja służy do podniesienia poziomu pewności, zanim przerwiemy pracę człowieka.

Narzędzia SAST są nadal bardzo ważne

Narzędzia SAST nadal doskonale sprawdzają się w egzekwowaniu standardów bezpiecznego kodowania, wychwytywaniu prostych problemów typu source-to-sink oraz wykrywaniu znanych wzorców na dużą skalę z przewidywalnymi kompromisami. Mogą stanowić istotny element dogłębnej obrony.

Ten wpis jest bardziej ukierunkowany: skupiamy się na tym, dlaczego agent zaprojektowany do rozumowania o zachowaniu i weryfikowania ustaleń nie powinien rozpoczynać pracy, opierając się na statycznej liście ustaleń.

Warto też zwrócić uwagę na powiązane ograniczenie czysto źródłowo-docelowego myślenia: nie każda luka jest problemem przepływu danych. Wiele rzeczywistych awarii to problemy ze stanem i niezmiennikami – obejścia przepływu pracy, luki w uwierzytelnieniach oraz błędy typu „system jest w niewłaściwym stanie”. W przypadku tego typu błędów skażona wartość nie dociera do ani jednego „niebezpiecznego ujścia”. Ryzyko polega na tym, co program zakłada, że zawsze będzie prawdą. 

Przyszłość

Oczekujemy, że ekosystem narzędzi bezpieczeństwa będzie się nadal rozwijać, a analiza statyczna, fuzzing, zabezpieczenia środowiska uruchomieniowego i agentowe przepływy pracy będą odgrywać swoje role.

Chcemy, aby Codex Security dobrze radził sobie z najbardziej problematycznym aspektem dla zespołów ds. bezpieczeństwa: zamienianiem informacji „to wygląda podejrzanie” na „to jest prawdziwy problem, jak zawodzi, a tu jest poprawka zgodna z intencją systemu”. 

Więcej informacji o tym, jak Codex Security skanuje repozytoria, weryfikuje wyniki i proponuje poprawki, znajdziesz w naszej dokumentacji(otwiera nowe okno).

Autor

OpenAI