< Powrót do Artykułów
◆ Case study · API BOLA / BFLA

API ufało ID.
Nie sprawdzało właściciela.

Techniczne case study BOLA w SaaS/API B2B: cross-tenant read, eksporty, joby, linki share, macierz testów i wzorzec naprawy autoryzacji.

TRYB black-box APIKLASA BOLA / BFLAOBSZAR multi-tenant SaaSWPŁYW cross-tenant data
API1
object-level
authorization
API5
function-level
authorization
XLSX
eksport danych
innego tenant-a
HIGH
ryzyko B2B
i reputacyjne

API ufało identyfikatorom bardziej niż tokenowi

W API najczęstszy błąd autoryzacji wygląda banalnie: użytkownik ma poprawny token, więc backend wykonuje operację na obiekcie wskazanym w URL. Problem w tym, że token mówi „kim jesteś”, ale nie mówi automatycznie „czy możesz dotknąć tego konkretnego raportu, faktury, projektu albo tenantId”.

Ten case jest zanonimizowany. Pokazujemy syntetyczne endpointy i response’y, które oddają mechanizm: separacja tenantów, eksporty, raporty, załączniki i role. Nie ma tu prawdziwych hostów, klientów ani identyfikatorów.

Najpierw rysujemy relacje

objects and relations
user_A -> tenant_RED -> project_RED -> report_RED -> attachment_RED user_B -> tenant_BLUE -> project_BLUE -> report_BLUE -> attachment_BLUE support_user -> selected tenants only admin_user -> all tenants, separate role # Test: czy user_A może dotknąć czegokolwiek z tenant_BLUE?

W systemach B2B sama rola „user” nie wystarczy. Najważniejszy jest tenant i relacja do obiektu. Błędy pojawiają się, gdy endpoint sprawdza tylko rolę, a nie sprawdza przynależności obiektu do organizacji użytkownika.

Raport innego tenant-a zwrócił 200 OK

request/response — cross-tenant read
GET /api/v2/reports/report_BLUE_001 HTTP/2 Host: api.example.invalid Authorization: Bearer token_user_A_from_tenant_RED Accept: application/json HTTP/2 200 OK { "reportId":"report_BLUE_001", "tenantId":"tenant_BLUE", "title":"Quarterly report [synthetic]", "status":"ready", "download":"/api/v2/reports/report_BLUE_001/export" } Expected: 403/404 Actual: 200 OK dla obiektu innego tenant-a
Kluczowy błąd

Backend sprawdzał, czy token jest ważny i czy użytkownik ma rolę user. Nie sprawdzał, czy report.tenantId należy do tenantów przypisanych do użytkownika.

Najgorsze luki często siedzą poza głównym endpointem

request/response — export bypass
POST /api/v2/reports/report_BLUE_001/export HTTP/2 Authorization: Bearer token_user_A_from_tenant_RED Content-Type: application/json {"format":"xlsx","includeDetails":true} HTTP/2 202 Accepted {"jobId":"job_EXPORT_REDACTED"} GET /api/v2/jobs/job_EXPORT_REDACTED/result HTTP/2 Authorization: Bearer token_user_A_from_tenant_RED HTTP/2 200 OK Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet Content-Length: 98231 [redacted binary xlsx]

To częsty wzorzec: endpoint główny bywa poprawiony, ale eksport, PDF, załącznik, job asynchroniczny albo link tymczasowy ma osobną ścieżkę kodu i słabszą autoryzację. Dlatego test BOLA musi obejmować cały lifecycle obiektu.

BOLA spotkało BFLA

role/function issue
DELETE /api/v2/reports/report_BLUE_001 HTTP/2 Authorization: Bearer token_user_A_from_tenant_RED HTTP/2 403 Forbidden POST /api/v2/reports/report_BLUE_001/share HTTP/2 Authorization: Bearer token_user_A_from_tenant_RED HTTP/2 200 OK {"shareId":"share_REDACTED","expiresAt":"2026-xx-xx"} [!] Delete był chroniony, share/export już nie.

To pokazuje różnicę między BOLA i BFLA. BOLA: czy mogę dotknąć obiektu innego tenant-a? BFLA: czy zwykły użytkownik może wykonać funkcję, która powinna być zarezerwowana dla administratora lub właściciela? W praktyce często występują razem.

Token mówi, kim jesteś.
Nie mówi automatycznie, do którego raportu masz prawo.

Co testujemy w API B2B

BOLA/BFLA test matrix
Object read list export attach share update delete report FAIL PASS FAIL n/a FAIL PASS PASS invoice PASS PASS FAIL FAIL n/a PASS PASS project PASS PASS n/a n/a PASS PASS PASS user PASS PASS n/a n/a n/a FAIL PASS PASS = poprawnie zablokowane albo brak danych FAIL = cross-tenant access confirmed # Raport nie ogranicza się do jednego endpointu. Pokazuje klasę problemu.
Severity: High / Critical

Jeżeli cross-tenant access obejmuje raporty, faktury, dokumenty, dane osobowe, eksporty lub linki share, ryzyko zwykle jest wysokie lub krytyczne. Szczególnie w SaaS B2B.

Najczęstsze powody

API1

BOLA

Brak sprawdzenia relacji user → tenant → object przy każdym odczycie, eksporcie i pobraniu.

MAPOWANIE → OWASP API1: Broken Object Level Authorization
API5

BFLA

Funkcje share/export/job dostępne dla roli, która nie powinna ich wykonywać na danym obiekcie.

MAPOWANIE → OWASP API5: Broken Function Level Authorization
JOBS

Asynchroniczne joby

Job exportu dziedziczył tylko token twórcy zadania, ale nie walidował ponownie obiektu przy generowaniu wyniku.

MAPOWANIE → Async Authorization
URL

Signed URLs

Link tymczasowy był wystawiany po słabej walidacji i potem działał bez kontekstu użytkownika.

MAPOWANIE → Access Control / Storage

Wzorzec autoryzacji dla multi-tenant API

1. Nie ufaj parametrom tenantId z requestu. Tenant użytkownika pochodzi z tokena/sesji i bazy, nie z URL-a albo body.

2. Query scoped by tenant. Zapytanie do bazy powinno zawierać warunek tenant_id wynikający z kontekstu użytkownika. Nie pobieraj obiektu globalnie, a potem „sprawdzaj później”.

3. Autoryzacja przy jobach. Job exportu musi przechowywać kontekst uprawnień i ponownie walidować dostęp przed wygenerowaniem oraz wydaniem wyniku.

4. Testy kontraktowe. Każdy endpoint z {id} powinien mieć test negatywny dla obiektu innego tenant-a.

safe query pattern
# risky: SELECT * FROM reports WHERE id = :report_id; # safer: SELECT * FROM reports WHERE id = :report_id AND tenant_id IN (:tenants_allowed_for_current_user); # if no row -> return 404/403 and do not reveal object existence
◆ BSTA · testpenetracyjny.pl

Budujesz SaaS B2B albo API dla klientów?

Sprawdzimy separację tenantów, BOLA/BFLA, eksporty, joby, signed URLs, załączniki i wszystkie ścieżki omijające UI.

Zamów test bezpieczeństwa →
Zakres + wycena w 2 dni robocze · raport techniczny + executive summary · pełna anonimizacja danych