Jedno konto kandydata.
Dostęp do cudzych CV.
Techniczne, zanonimizowane case study z testów penetracyjnych systemu rekrutacyjnego: macierz uprawnień, IDOR/BOLA, request/response i naprawa klasy błędów, a nie jednego endpointu.
Level Authorization
pełnego PoC
być 403/404
utrata zaufania
Nie musisz przejąć admina, żeby zrobić incydent
W systemach rekrutacyjnych najcenniejsze dane często nie siedzą w panelu administratora, tylko w zwykłych obiektach użytkownika: CV, załącznikach, notatkach, zgłoszeniach i statusach procesu. Jeżeli backend przyjmuje identyfikator dokumentu i nie sprawdza właściciela, zwykłe konto kandydata może stać się narzędziem do masowego odczytu cudzych danych.
Ten opis jest zanonimizowany i przepisany tak, aby nie ujawniać klienta, branży, hostów, prawdziwych endpointów ani identyfikatorów. Requesty i response’y pokazują mechanizm testu, a nie produkcyjne dane. Wszystkie wartości są syntetyczne: cand_AAAA, doc_BBBB, tenant-redacted, example.invalid.
Pokażemy, jak myślimy podczas testów BOLA/IDOR: od mapowania modelu danych, przez macierz uprawnień, po potwierdzenie wpływu bez pobierania realnych dokumentów klienta. To ma być merytoryczny case study, a nie gotowy exploit przeciwko konkretnemu systemowi.
Dwa konta, zero admina, pełna autoryzacja projektu
Dostaliśmy dwa zwykłe konta testowe. Konto A miało utworzone CV i jedną aplikację. Konto B było świeże, bez dokumentów. Nie było kodu źródłowego, dokumentacji API ani kont pracowników. Test był black-box z perspektywy normalnego użytkownika.
Pierwsze 30 minut: model obiektów z samej przeglądarki
Nie zaczęliśmy od fuzzingu. Najpierw normalnie użyliśmy aplikacji i zapisaliśmy przepływy: upload CV, lista dokumentów, podgląd zgłoszenia, pobranie załącznika, usunięcie dokumentu. W Burp Suite powstała prosta mapa: kandydat → dokument → zgłoszenie → załącznik.
Już tutaj widać kilka rzeczy istotnych dla pentestera: obiekty mają własne identyfikatory, API używa jawnego candidateId, odpowiedź zawiera ownerId, a pobranie dokumentu jest osobnym endpointem. To naturalny punkt do testu autoryzacji obiektowej.
Jeżeli endpoint /documents/{documentId} bierze identyfikator i nie porównuje ownerId z użytkownikiem z tokena, mamy IDOR. Jeżeli problem dotyczy wielu obiektów, mamy systemowy BOLA, a nie pojedynczy błąd.
Nie testujemy jednego requestu. Testujemy relacje.
Dla każdego obiektu zbudowaliśmy mini-macierz: czy konto A może czytać A, czy konto B może czytać A, czy konto B może pobrać plik A, czy konto B może usunąć A, czy konto B może użyć dokumentu A w swoim zgłoszeniu. To ważne, bo część aplikacji blokuje zapis, ale zapomina o odczycie albo eksporcie.
Konto B czyta metadane dokumentu konta A
Najpierw potwierdziliśmy błąd bez pobierania pliku. To dobra praktyka: jeżeli sama odpowiedź z metadanymi dokumentu wystarcza do potwierdzenia naruszenia kontroli dostępu, nie trzeba dotykać realnego załącznika. W raporcie da się pokazać wpływ i minimalizować ekspozycję danych.
Dla obiektów prywatnych często lepiej zwracać generyczne 404, żeby nie potwierdzać istnienia zasobu. Kluczowe jest jednak nie samo 403/404, ale egzekwowanie autoryzacji po stronie serwera.
Metadane były tylko początkiem
Drugi test dotyczył pobrania pliku. Response z metadanymi zawierał URL pobierania. Sprawdziliśmy, czy token B zostanie ponownie zweryfikowany przy endpointzie plikowym, czy aplikacja zakłada, że skoro ktoś dostał URL, to już może pobrać plik.
To był moment, w którym podatność z „błędu w metadanych” stała się pełnym naruszeniem poufności dokumentów. Technicznie: brak sprawdzenia subject(token) == owner(document) w warstwie dokumentów i/lub plików.
CV może zawierać imię, nazwisko, telefon, adres e-mail, zdjęcie, historię zatrudnienia, edukację, linki do profili i czasem dane szczególnych kategorii. Skala zależy od możliwości enumeracji identyfikatorów i liczby obiektów. W systemach rekrutacyjnych to realny problem RODO.
To tylko dłuższy identyfikator.
Gdzie zwykle leży błąd
Autoryzacja obiektu tylko w UI
Frontend ukrywał cudze dokumenty, ale backend akceptował dowolny identyfikator dokumentu. UI nie jest boundary bezpieczeństwa.
Nadmierne ujawnianie pól
Endpoint zwracał ownerId, downloadUrl i metadane, których UI nie potrzebował w każdym kontekście. To ułatwia rekonesans i łańcuchowanie.
Różne reguły dla odczytu i zapisu
Delete/update były blokowane, ale read/download już nie. To typowy błąd: zespół chroni operacje modyfikujące, a nie docenia poufności odczytu.
Jak zamknąć tę klasę błędów, a nie jeden endpoint
1. Centralna funkcja autoryzacji obiektowej. Każdy endpoint przyjmujący ID zasobu powinien przejść przez wspólny mechanizm: pobierz obiekt → sprawdź tenant → sprawdź właściciela/rolę → dopiero potem zwróć dane.
2. Testy regresyjne BOLA. Dla każdego typu obiektu warto mieć automatyczny test: user A tworzy obiekt, user B próbuje read/update/delete/export/download. To powinno działać w CI.
3. Minimalizacja odpowiedzi. Nie zwracaj ownerId, downloadUrl, storageKey, internalStatus, jeżeli UI tego nie potrzebuje. Zmniejszasz powierzchnię rekonesansu.
Nie tylko “jest IDOR”, ale ile to kosztuje biznesowo
W raporcie technicznym pokazujemy requesty, macierz, root cause i rekomendacje. W executive summary tłumaczymy wpływ: potencjalny dostęp do danych kandydatów, ryzyko zgłoszenia naruszenia, utrata zaufania, obowiązki prawne, priorytet naprawy i zakres retestu.
To jest dokładnie typ luki, której nie widać na demo aplikacji. Wszystko wygląda poprawnie, dopóki ktoś nie wyjdzie poza ścieżkę UI i nie zacznie używać API tak, jak używałby go napastnik.
Masz API z dokumentami, plikami albo tenantami?
Sprawdzimy, czy Twoja aplikacja egzekwuje uprawnienia przy każdym obiekcie: od UI, przez API, po eksporty, załączniki i linki do plików.
Zamów test bezpieczeństwa →