API jest miejscem, w którym aplikacja przestaje udawać interfejs, a zaczyna pokazywać prawdziwe reguły biznesowe. Frontend może ukrywać przyciski, filtrować listy i prowadzić użytkownika po zaplanowanej ścieżce. API musi jednak egzekwować prawa dostępu, walidować dane i chronić obiekty niezależnie od tego, co zrobi przeglądarka lub aplikacja mobilna.
W praktyce najpoważniejsze błędy API to nie zawsze SQL injection albo klasyczne RCE. Bardzo często są to podatności wynikające z założenia, że użytkownik będzie klikał tylko to, co pokazuje frontend. Tester API nie klika. Tester manipuluje requestami, rolami, tenantami, obiektami, metodami HTTP, nagłówkami i kolejnością operacji.
Dlaczego API jest tak atrakcyjnym celem
API często ma bezpośredni dostęp do danych i funkcji biznesowych. Endpointy obsługują faktury, zamówienia, użytkowników, eksporty, dokumenty, płatności, integracje, powiadomienia, konfigurację konta i funkcje administracyjne. Jeżeli kontrola dostępu jest niepełna, atakujący nie musi łamać skomplikowanej kryptografii. Wystarczy, że znajdzie obiekt, którego nie powinien widzieć.
Najczęstszy schemat błędu wygląda prosto: frontend pobiera tylko obiekty przypisane do użytkownika, ale backend nie sprawdza właściciela obiektu przy każdym odczycie lub modyfikacji. W efekcie zmiana ID w URL, body albo parametrze GraphQL daje dostęp do cudzych danych.
BOLA / IDOR — najważniejszy problem API
BOLA, czyli Broken Object Level Authorization, jest jedną z najbardziej praktycznych klas podatności API. Nie chodzi o to, że endpoint istnieje. Chodzi o to, że endpoint przyjmuje identyfikator obiektu i nie weryfikuje, czy aktualny użytkownik ma prawo do tego obiektu.
GET /api/v2/customers/4912/contracts/884/download HTTP/1.1
Authorization: Bearer token_klienta_A
# Test kontrolny:
GET /api/v2/customers/4913/contracts/885/download HTTP/1.1
Authorization: Bearer token_klienta_A
Jeżeli drugi request zwraca dokument klienta B, problem jest poważny. W raporcie warto wskazać nie tylko podatny endpoint, ale również wzorzec architektoniczny: gdzie w kodzie powinna powstać centralna kontrola dostępu, jak testować obiekty w innych modułach i jakie kryterium musi spełnić retest.
Broken Function Level Authorization
Druga krytyczna kategoria to sytuacja, w której użytkownik bez odpowiedniej roli może wykonać funkcję zarezerwowaną dla administratora, operatora lub właściciela konta. Frontend może nie pokazywać przycisku, ale endpoint nadal działa.
POST /api/admin/users/728/disable HTTP/1.1
Authorization: Bearer token_zwyklego_uzytkownika
Content-Type: application/json
{"reason":"test"}
Poprawna aplikacja powinna zwrócić odmowę. Jeżeli akcja zostaje wykonana, problem dotyczy modelu autoryzacji funkcji. Ten typ błędu jest szczególnie groźny w systemach B2B, panelach administracyjnych, SaaS wielodostępnym i aplikacjach z rozbudowaną macierzą ról.
Nadmiarowe dane w odpowiedziach
API często zwraca więcej danych, niż pokazuje frontend. Developer zakłada, że „tego nie widać w UI”, ale odpowiedź HTTP zawiera pola techniczne, role, flagi, numery telefonów, adresy e-mail, wewnętrzne identyfikatory, statusy płatności albo dane innych użytkowników.
GET /api/account/profile HTTP/1.1
{
"name": "Jan Kowalski",
"email": "[email protected]",
"isAdmin": false,
"internalRiskScore": 87,
"passwordResetTokenLast": "...",
"tenantBillingId": "..."
}
Czasem takie dane nie są od razu podatnością krytyczną, ale budują wiedzę potrzebną do kolejnych ataków. Dobra rekomendacja brzmi: API powinno zwracać minimalny model danych dla danej funkcji, a nie pełny obiekt domenowy „bo tak wygodniej”.
GraphQL: elastyczność, która wymaga kontroli
GraphQL jest wygodny, ale w testach bezpieczeństwa wymaga osobnego podejścia. Trzeba sprawdzić introspection, autoryzację na poziomie resolverów, zagnieżdżenia, koszt zapytań, batchowanie, możliwość pobierania pól ukrytych oraz błędy w modelu typów.
query {
user(id: "1002") {
id
email
roles
invoices { id amount downloadUrl }
}
}
W GraphQL szczególnie łatwo popełnić błąd polegający na sprawdzeniu uprawnień tylko na najwyższym obiekcie, a nie na polach i relacjach. Tester powinien pracować na kilku kontach, kilku rolach i danych należących do różnych tenantów. Bez tego wynik będzie powierzchowny.
Rate limiting i abuse cases
API obsługuje operacje, które mogą być kosztowne lub wrażliwe: logowanie, reset hasła, wysyłka SMS, generowanie raportów, eksport plików, wyszukiwarki, walidacja kodów rabatowych, sprawdzanie numerów dokumentów. Brak limitów może prowadzić do enumeracji, nadużyć kosztowych, DoS aplikacyjnego albo wycieku informacji.
Dobry pentest API nie kończy się pytaniem „czy jest limit requestów na sekundę”. Trzeba zrozumieć, które operacje są biznesowo kosztowne, które ujawniają różnice w odpowiedziach i które można automatyzować bez blokady.
| Test | Przykładowe ryzyko | Dobra rekomendacja |
|---|---|---|
| Reset hasła | enumeracja kont, spam, przejęcie procesu | limity per konto, IP, fingerprint, jednolite odpowiedzi |
| Eksport danych | masowe pobranie dokumentów | autoryzacja obiektów, kolejka, audyt, limity wolumenu |
| GraphQL | kosztowne zapytania i zbyt głębokie relacje | limit głębokości, kosztu, timeout, allowlisty operacji |
| Webhook test | SSRF i odpytywanie zasobów wewnętrznych | allowlista, blokada IP prywatnych, kontrola redirectów |
Jak przygotować API do testu
Najlepszy efekt daje przygotowanie środowiska testowego z realistycznymi danymi, dokumentacją endpointów, kontami o różnych rolach i opisem logiki biznesowej. Dokumentacja OpenAPI/Swagger, kolekcja Postman lub przykładowe requesty bardzo skracają czas rekonesansu, ale nie zastępują testu. Tester i tak powinien porównać dokumentację z rzeczywistym ruchem aplikacji.
Warto przygotować minimum trzy typy kont: użytkownik zwykły, użytkownik o wyższych uprawnieniach oraz użytkownik z innej organizacji/tenanta. Jeżeli aplikacja ma panele administracyjne, partnerów, operatorów, oddziały albo proces akceptacji, każdy z tych kontekstów zwiększa wartość testu.
Co powinien zawierać raport z pentestu API
Raport powinien zawierać macierz ról, opis testowanych endpointów, przykłady request/response, dowody wpływu, klasyfikację ryzyka i rekomendacje na poziomie wzorca, nie tylko pojedynczego adresu URL. Przy błędach BOLA naprawa jednego endpointu często nie wystarcza. Trzeba sprawdzić całą klasę endpointów i wprowadzić centralny model autoryzacji.
Dlaczego API wymaga osobnego zakresu testów
API bywa traktowane jako dodatek do aplikacji webowej, ale z perspektywy bezpieczeństwa często jest najważniejszą częścią systemu. To tam wykonywane są operacje na obiektach, generowane eksporty, zapisywane dokumenty, uruchamiane integracje i egzekwowane role. Jeżeli API nie sprawdza uprawnień po stronie backendu, frontend nie zapewni bezpieczeństwa.
Co sprawdzamy w API REST i GraphQL
- czy użytkownik może odczytać, zmienić lub usunąć obiekty należące do innego użytkownika,
- czy role i tenanty są egzekwowane przy każdej operacji,
- czy GraphQL nie pozwala na introspekcję, nadmiarowe zapytania lub obchodzenie ograniczeń,
- czy rate limiting działa dla logowania, resetu hasła, eksportów i kosztownych operacji,
- czy API nie zwraca pól, których frontend nie pokazuje, ale które mają znaczenie biznesowe,
- czy integracje i webhooki nie otwierają drogi do SSRF, nadużyć i wycieku danych.
W raporcie dla API szczególnie ważna jest macierz uprawnień: kto, na jakiej roli, do jakiego obiektu, w jakim tenantcie i w jakiej akcji powinien mieć dostęp. Bez tego łatwo naprawić jeden endpoint, a zostawić tę samą klasę błędu w dziesięciu innych miejscach.