Jak testować REST API - metodyka od inwentaryzacji do raportu
W skrócie: w OWASP Web Security Testing Guide v4.2 rozdział 4.12 nazywa się „API Testing" i zawiera dokładnie jeden podrozdział: testowanie GraphQL. Dla REST nie ma osobnego rozdziału i nie jest to luka w dokumencie. Test REST API przechodzi przez te same jedenaście rozdziałów co test aplikacji webowej - zmienia się tylko to, że zamiast formularzy i ekranów mamy metodę, ścieżkę, nagłówki i ciało żądania.
Praktyczna konsekwencja: nie ma „skanu API". Jest systematyczne przejście przez punkty końcowe, które trzeba najpierw policzyć.
W poprzednim wpisie o bezpieczeństwie API opisaliśmy co się w API psuje - dziesięć kategorii OWASP API Security Top 10. Ten wpis jest o tym, jak się tego szuka: w jakiej kolejności, czego test wymaga na wejściu i które etapy da się zautomatyzować, a które nie.
Dlaczego REST nie ma własnego rozdziału w WSTG
Spis treści rozdziału czwartego WSTG to kolejno: zbieranie informacji, konfiguracja i wdrożenie, zarządzanie tożsamością, uwierzytelnianie, autoryzacja, zarządzanie sesją, walidacja wejścia, obsługa błędów, słaba kryptografia, logika biznesowa, strona kliencka - i na końcu API. GraphQL dostał osobny podrozdział (WSTG-APIT-01), bo wprowadza mechanizmy, których w HTTP nie ma: introspekcję schematu, zagnieżdżanie zapytań, grupowanie wielu zapytań w jednym żądaniu. REST nie wprowadza nic nowego poza samym HTTP, więc testuje się go rozdziałami od pierwszego do jedenastego.
Z tego wynika cała metodyka. Test REST API to nie osobna dyscyplina, tylko ten sam test co dla aplikacji webowej, prowadzony bez pośrednictwa interfejsu. I właśnie dlatego jest trudniejszy do zaplanowania: w aplikacji z przeglądarką powierzchnię ataku widać na ekranie, w API trzeba ją najpierw zrekonstruować.
Krok 1: inwentaryzacja, czyli policzenie punktów końcowych
Pierwszy etap nie jest atakiem, tylko spisem. Specyfikacja OpenAPI, jeśli istnieje, jest punktem wyjścia, ale nigdy nie jest kompletna - dokumentacja zostaje w tyle za kodem, a najciekawsze punkty końcowe to zwykle te, o których zapomniano. Dlatego spis składamy z czterech źródeł jednocześnie:
- Specyfikacja - OpenAPI, Swagger, kolekcja Postman, cokolwiek jest.
- Ruch z prawdziwego klienta - przechwycony ruch aplikacji frontendowej i mobilnej. To często jedyne wiarygodne źródło: pokazuje, co realnie jest wywoływane, a nie co było zaplanowane.
- Starsze wersje - jeżeli działa
/api/v2/, sprawdzamy, czy/api/v1/nie odpowiada nadal, ze starą logiką autoryzacji i bez poprawek z ostatnich dwóch lat. - Zgadywanie po wzorcu - nazwy w REST są przewidywalne. Jeżeli istnieje
GET /users/{id}, warto sprawdzić, czy nie istnieje teżGET /userszwracające komplet.
Z praktyki
Rozbieżność między specyfikacją a rzeczywistością to osobne znalezisko, nawet gdy żaden z nieudokumentowanych punktów końcowych nie okazuje się podatny. Punkt końcowy, o którym nikt nie pamięta, nie trafi też do przeglądu kodu ani do testów regresji przy następnej zmianie uprawnień.
Krok 2: metody HTTP, nie tylko ścieżki
Jeden adres to tyle testów, ile obsługuje metod. Specyfikacja HTTP (RFC 9110) dzieli metody na bezpieczne, czyli z definicji tylko czytające - GET, HEAD, OPTIONS, TRACE - oraz idempotentne, dla których powtórzenie żądania zostawia serwer w tym samym stanie; do bezpiecznych dochodzą tu PUT i DELETE. Jedyną metodą, która nie jest ani bezpieczna, ani idempotentna, jest POST.
| Co sprawdzamy | Dlaczego |
|---|---|
| Każda metoda na każdej ścieżce | Autoryzacja bywa pilnowana tylko na metodzie używanej przez interfejs. GET /orders/{id} może być zabezpieczone, a DELETE na tym samym adresie - nie |
Odpowiedź 405 i nagłówek Allow | RFC 9110 wymaga, by odpowiedź 405 zawierała nagłówek Allow. Serwer sam wtedy wylicza obsługiwane metody, co uzupełnia inwentaryzację z kroku pierwszego |
| Nadpisanie metody nagłówkiem | Nagłówki typu X-HTTP-Method-Override potrafią przepuścić DELETE przez regułę, która filtruje tylko po metodzie żądania widzianej przez serwer proxy |
| Powtórzenie żądania niezmieniającego stanu | Jeżeli PUT albo DELETE nie są idempotentne w praktyce, dwukrotne wysłanie tego samego żądania daje efekt, którego nikt nie przewidział |
Krok 3: uwierzytelnianie i cykl życia tokenu
Tu interesuje nas nie samo logowanie, a to, co dzieje się z tokenem później. Czy wygasa i po jakim czasie. Czy wylogowanie faktycznie go unieważnia po stronie serwera, czy tylko usuwa z przeglądarki. Czy podpis jest weryfikowany, czy wystarczy podmienić zawartość. Czy token wydany dla jednego środowiska działa w drugim.
Osobny sygnał to sposób rozróżniania kodów 401 i 403. RFC 9110 przypisuje im różne znaczenia: 401 oznacza brak prawidłowych danych uwierzytelniających, 403 - że serwer zrozumiał żądanie i odmawia jego wykonania. Gdy API zwraca 401 tam, gdzie użytkownik jest poprawnie zalogowany, ale nie ma uprawnień do zasobu, zwykle znaczy to, że uwierzytelnianie i autoryzacja są obsługiwane w jednym miejscu i nie zawsze rozdzielane. To podpowiedź, gdzie szukać dalej w kroku czwartym.
Krok 4: autoryzacja z dwóch kont równolegle
Ten krok jest sednem całego testu i nie da się go wykonać jednym kontem. Pracujemy na co najmniej dwóch kontach tej samej roli (konto A i konto B, oba zwykli użytkownicy, każde z własnymi danymi) oraz po jednym koncie na każdą wyższą rolę. Następnie dla każdego punktu końcowego przyjmującego identyfikator wykonujemy to samo żądanie trzy razy: jako właściciel zasobu, jako obce konto tej samej roli i bez żadnego tokenu.
Powstaje z tego macierz, którą się wypełnia punkt po punkcie. Nudna, ale to właśnie w niej wychodzi Broken Object Level Authorization - pierwsza pozycja listy OWASP i podatność, której żaden skaner nie wykryje, bo narzędzie automatyczne nie wie, które dane należą do kogo. Szczegółowo opisaliśmy ją w wpisie o OWASP API Security Top 10.
Krok 5: walidacja wejścia, czyli nie tylko wartości
W aplikacji z formularzem pole tekstowe przyjmuje tekst. W API klient wysyła JSON, więc testujemy nie tylko wartości, ale i typy oraz strukturę: liczbę tam, gdzie oczekiwany jest łańcuch znaków, tablicę w miejsce obiektu, null, pole zagnieżdżone głębiej niż przewidziano, pole dopisane spoza formularza. Osobno sprawdzamy podmianę nagłówka Content-Type, bo ta sama treść potrafi trafić do innego parsera i obejść walidację zrobioną na poziomie jednego formatu.
Najczęściej pomijane wejście to parametry pomocnicze. Paginacja, sortowanie i filtrowanie często trafiają wprost do zapytania do bazy danych, bo nikt nie traktuje ich jako danych od użytkownika. Parametr sort albo limit zasługuje na tę samą uwagę co pole w treści żądania, a limit bez górnego ograniczenia to gotowy sposób na wyciągnięcie całej tabeli jednym żądaniem.
Krok 6: logika biznesowa i kolejność żądań
Ostatni krok wykorzystuje to, czego nie było w interfejsie: pełną kontrolę nad kolejnością i czasem. Interfejs prowadzi użytkownika przez kroki jeden po drugim, API przyjmuje każdy z nich osobno. Pomijamy więc krok środkowy procesu i sprawdzamy, czy końcowy go wymaga. Wysyłamy to samo żądanie kilka razy równolegle, żeby zobaczyć, czy sprawdzenie warunku i jego zmiana to jedna operacja, czy dwie. Zmieniamy w kroku końcowym wartość ustaloną w kroku wcześniejszym - cenę, kwotę, identyfikator odbiorcy - zakładając, że serwer pamięta ją z poprzedniego żądania. Często nie pamięta i przyjmuje tę przysłaną.
Co trzeba przygotować przed startem
- Co najmniej dwa konta na każdą rolę z osobnymi, rozróżnialnymi danymi. Bez tego krok czwarty nie istnieje.
- Specyfikacja, jeśli jest - przyspiesza inwentaryzację, ale jej brak nie blokuje testu.
- Środowisko z danymi przypominającymi produkcję. Puste API nie pokazuje nic: błędy autoryzacji widać dopiero wtedy, gdy jest czyje dane podejrzeć.
- Ustalenie limitów żądań - czy mają zostać włączone (wtedy je testujemy), czy podniesione dla adresu testującego (wtedy testujemy resztę szybciej).
- Kontakt awaryjny na wypadek, gdy test wywoła skutek uboczny w systemie podłączonym dalej.
Szerzej o przygotowaniu zakresu, okna czasowego i dostępów pisaliśmy we wpisie jak przygotować firmę do pentestu.
Co z tego da się zautomatyzować
Inwentaryzację - częściowo. Sprawdzenie metod na każdej ścieżce, nagłówki bezpieczeństwa, oczywiste błędy konfiguracji, fuzzing typów w JSON - to robią narzędzia i warto, żeby robiły. W testach penetracyjnych API używamy ich właśnie na tych etapach, żeby czas zostawić na resztę. Kroki czwarty i szósty zostają człowiekowi, bo oba wymagają wiedzy o tym, co w danym systemie jest poprawnym stanem: do kogo należy zasób numer 1041 i czy zamówienie może przejść do realizacji bez opłacenia. Narzędzie widzi odpowiedź 200 OK i uznaje ją za sukces. Tester patrzy, czyje dane są w treści tej odpowiedzi.
Chcecie sprawdzić swoje REST API?
Przechodzimy przez każdy punkt końcowy z dwóch kont o różnych uprawnieniach i opisujemy, co da się z niego wyciągnąć. Napiszcie - odpiszemy w 24 h.
Zapytaj o wycenę