Przykładowy raport
Tak wygląda raport z Code Audit, od pierwszej do ostatniej strony.
Kompletny przykładowy raport dla fikcyjnej aplikacji. Zobaczysz strukturę i szczegółowość, zanim zamówisz własny. Aplikacja jest zmyślona, ale dane o podatnościach pochodzą z prawdziwego przebiegu composer audit.
Przykładowy raport, fikcyjna aplikacja
„Acme Fleet Portal” i Acme Logistics nie istnieją. Kod, metryki i ustalenia dotyczące konfiguracji w tym raporcie są zmyślone, żeby pokazać strukturę i szczegółowość Code Audit w Refbergu. Dane o podatnościach są prawdziwe: 24 października 2026 uruchomiliśmy composer audit na pliku lock z wersjami paczek wymienionymi w punkcie 4.1. Liczby zgłoszeń, identyfikatory CVE i wersje z poprawkami pochodzą z tego przebiegu. Ceny to przykłady oparte na naszych opublikowanych cenach wyjściowych.
Raport z Code Audit
| Klient | Acme Logistics Sp. z o.o. (firma fikcyjna) |
|---|---|
| Aplikacja | Acme Fleet Portal: rezerwacje, fakturowanie i API JSON dla aplikacji mobilnej kierowców |
| Rozmiar audytu | M (od 50 000 do 200 000 linii kodu PHP) |
| Dostęp | repozytorium tylko do odczytu, pliki .env stagingu i produkcji (z usuniętymi sekretami), pliki konfiguracyjne serwera, crontab |
| Data | 24 października 2026 |
| Przygotował | Refberg |
1. Podsumowanie dla zarządu
Portal działa i zarabia, ale stoi na oprogramowaniu, które przestało dostawać poprawki bezpieczeństwa między 2022 a 2025 rokiem. Trzy problemy wymagają działania jeszcze w tym tygodniu, przed jakąkolwiek aktualizacją:
- Serwer stagingowy można przejąć z internetu. Jest publicznie dostępny, działa w trybie debugowania i ma komponent z opublikowaną luką pozwalającą na zdalne wykonanie kodu (CVE-2021-3129). Na stagingu leży cotygodniowa kopia produkcyjnej bazy danych, razem z nazwiskami, adresami i fakturami klientów.
- Hasła produkcyjne i klucz szyfrowania aplikacji są w historii Git. Może je odczytać każdy, kto ma albo kiedykolwiek miał kopię repozytorium. Nie były zmieniane od 2021 roku.
- Kopie zapasowe leżą na tym samym serwerze co baza danych i nigdy nie zostały odtworzone. Awaria dysku albo ransomware na tym serwerze zabrałyby dane razem z kopiami.
Wszystkie trzy da się zamknąć w jeden do dwóch dni, bez zmian w kodzie aplikacji (punkt 6, etap 0).
Problem długoterminowy to sama platforma: PHP 7.4 (koniec wsparcia: 28 listopada 2022) i Laravel 6 (koniec poprawek bezpieczeństwa: 6 września 2022). Skan zależności znalazł 114 opublikowanych zgłoszeń bezpieczeństwa w 20 paczkach. Część da się załatać wydaniami poprawkowymi w obrębie Laravela 6. Większość wymaga aktualizacji.
Testów automatycznych prawie nie ma (6,1% pokrycia linii, żadnego testu na sześciu krytycznych ścieżkach biznesowych), więc każda zmiana jest dziś strzałem na ślepo. Rekomendujemy wariant A: najpierw siatka bezpieczeństwa, potem aktualizacja w dwóch etapach ze stałą ceną do Laravela 13 na PHP 8.4, w ciągu około czterech miesięcy, a od 5. miesiąca Care Standard. Wariant B (zostać na Laravelu 6 z łatanym PHP) kosztuje w tym roku mniej. Punkt 6 wyjaśnia, kiedy ma sens.
2. Zakres i metoda
W zakresie: kod aplikacji w repozytorium (gałąź main, commit 4f1c9e2), composer.json i composer.lock, historia Git, pliki konfiguracyjne, konfiguracja serwera przekazana przez klienta, skrypt wdrożeniowy.
Narzędzia: composer audit (Composer 2, baza zgłoszeń Packagist), PHPStan 2.x bez rozszerzenia dla Laravela (Larastan dla Laravela 6 istnieje tylko dla starej linii PHPStan 0.12, więc analizę z Larastanem powtarzamy po aktualizacji frameworka), Rector 2.x w trybie dry run, PHP_CodeSniffer z PHPCompatibility, PHPUnit z PCOV do pomiaru pokrycia, gitleaks do szukania sekretów w historii, git log do częstotliwości zmian. Potem ręczny przegląd routingu, uwierzytelniania, przesyłania plików, generowania PDF, kolejek i zadań cyklicznych.
Poza zakresem: testy penetracyjne, testy obciążeniowe, przegląd aplikacji mobilnej, systemy operatora płatności i konfiguracja sieci u dostawcy hostingu. Niczego nie próbowaliśmy wykorzystać. Gdy piszemy, że podatność jest możliwa do wykorzystania, mamy na myśli, że w Twojej konfiguracji spełnione są jej opublikowane warunki.
3. Mapa ryzyk
Prawdopodobieństwo i skutek oceniamy w skali od 1 (niski) do 3 (wysoki). Priorytet: P0 w tym tygodniu, P1 w pierwszym etapie, P2 w trakcie aktualizacji.
| ID | Ryzyko | Prawdopodobieństwo | Skutek | Priorytet |
|---|---|---|---|---|
| R1 | Zdalne wykonanie kodu na publicznym stagingu przez Ignition (CVE-2021-3129); na stagingu są produkcyjne dane osobowe | 3 | 3 | P0 |
| R2 | APP_KEY oraz hasła do bazy i poczty widoczne w historii Git od 2021 roku | 3 | 3 | P0 |
| R3 | Kopie zapasowe na tym samym serwerze, nigdy nieodtworzone | 2 | 3 | P0 |
| R4 | Generowanie PDF z włączonymi zasobami zdalnymi w dompdf 0.8.6 (CVE-2022-28368, zdalne wstrzyknięcie kodu przez czcionki) | 2 | 3 | P0 |
| R5 | Brak testów na krytycznych ścieżkach; 9 z 42 istniejących testów nie przechodzi | 3 | 2 | P1 |
| R6 | PHP 7.4 z Ubuntu 20.04 bez Ubuntu Pro: od maja 2025 brak aktualizacji bezpieczeństwa środowiska uruchomieniowego | 2 | 3 | P1 |
| R7 | Laravel 6 po końcu wsparcia; 5 zgłoszeń dotyczących frameworka nie ma poprawki w linii 6.x | 2 | 2 | P2 |
| R8 | Porzucone lub zastąpione paczki (SwiftMailer, Faker, Ignition 1.x) | 2 | 2 | P2 |
| R9 | Całą wiedzę miał jeden były programista; brak runbooka, wdrożenie to skrypt shell uruchamiany ręcznie | 3 | 2 | P1 |
4.1 Zależności
Wynik: 114 zgłoszeń w 20 paczkach: 13 krytycznych, 44 wysokie, 50 średnich, 7 niskich. 110 z nich dotyczy zależności produkcyjnych. Wybrane paczki:
| Paczka | Zainstalowana | Zgłoszenia | Najważniejsze zgłoszenia | Pierwsza wersja z poprawką | Do naprawy w obrębie Laravela 6? |
|---|---|---|---|---|---|
laravel/framework | 6.20.0 | 11 | CVE-2021-21263 (wiązanie parametrów zapytań), CVE-2021-43808 (XSS w Blade), CVE-2024-52301 (środowisko przez query string) | 6.20.45 dla 6 z 11 | częściowo: 6.20.45 |
facade/ignition (dev) | 1.16.3 | 3 | CVE-2021-3129 (zdalne wykonanie kodu) | 1.16.14 (1.16.15 dla wszystkich trzech) | tak; poza tym nie może być zainstalowana na produkcji |
dompdf/dompdf | 0.8.6 | 14 | CVE-2022-28368 (zdalne wstrzyknięcie kodu przez czcionki), CVE-2021-3838 (deserializacja) | 1.2.1 / 2.0.x / 3.1.6 | częściowo: wymaga aktualizacji laravel-dompdf do nowej wersji głównej |
guzzlehttp/guzzle | 6.5.5 | 14 | CVE-2022-31090, CVE-2022-31091 (dane uwierzytelniające zachowane po przekierowaniu do innej domeny) | 6.5.8 dla 5 z 14 | częściowo |
firebase/php-jwt | 5.2.0 | 2 | CVE-2021-46743 (pomylenie klucza i algorytmu) | 6.0.0 | tak, ze zmianami w kodzie (nowe API) |
league/flysystem | 1.0.70 | 2 | CVE-2021-32708 (race condition przy zapisie plików) | 1.1.4 | tak |
phpoffice/phpspreadsheet | 1.15.0 | 29 | XXE w czytniku XLSX (CVE-2024-45293, CVE-2024-48917) | 1.29.4 i nowsze (dla obu luk XXE) | prawdopodobnie, przez nowszy maatwebsite/excel 3.1.x; do potwierdzenia w etapie 1 |
swiftmailer/swiftmailer | 6.2.3 | 1 | CVE-2024-28859 (gadżet deserializacji) | 6.2.5 | tak, ale paczka jest porzucona |
Porzucone lub zastąpione: swiftmailer/swiftmailer jest oznaczony na Packagist jako porzucony na rzecz symfony/mailer (Laravel 9 przeszedł na niego). fzaninotto/faker jest oznaczony jako porzucony. facade/ignition zastąpił spatie/laravel-ignition; ostatnie wydanie ukazało się w styczniu 2023. fideloper/proxy nie miał wydania od lutego 2022, a jego funkcje przeszły do Laravela 9.
Dlaczego to ważniejsze niż sama liczba: groźne są te zgłoszenia, których warunki spełniasz. W tej aplikacji to Ignition na stagingu (R1, tryb debugowania jest włączony), dompdf z enable_remote => true w config/dompdf.php (R4, szablon faktury ładuje logo z adresu URL) i php-jwt w API mobilnym (bez wglądu w konfigurację kluczy nie mogliśmy potwierdzić, czy lukę da się wykorzystać; patrz 4.4).
4.2 Testy i CI
- 42 testy PHPUnit, z czego 9 nie przechodzi na
main. Błędy pochodzą z 2023 roku i od tego czasu nikt testów nie uruchamiał. - Pokrycie linii 6,1%. Pokryte są dwie klasy pomocnicze i formularz logowania.
- Krytyczne ścieżki bez żadnego testu: tworzenie rezerwacji, wyliczanie ceny, generowanie faktury (PDF), callback płatności, uwierzytelnianie w API kierowców, miesięczny eksport faktur (XLSX).
- Brak pipeline'u CI.
deploy.shuruchamia na produkcjigit pull,composer install(bez--no-dev, dlatego Ignition i Faker są na serwerach) iphp artisan migrate --force, w tej kolejności i bez kroku kopii zapasowej.
4.3 Analiza statyczna i gotowość do aktualizacji
| Sprawdzenie | Wynik |
|---|---|
| PHPStan, poziom 0 (bez rozszerzenia dla Laravela) | 286 błędów. 41 to wywołania niezdefiniowanych metod lub funkcji, z czego co najmniej 7 na ścieżkach osiągalnych z routingu |
| PHPStan, poziom 5 (bez rozszerzenia dla Laravela) | 1912 błędów. Rekomendujemy baseline na poziomie 5 i zero nowych błędów od etapu 1 |
| PHPCompatibility, cel PHP 8.4 | 37 błędów, 112 ostrzeżeń |
| Rector dry run, zestaw reguł PHP 8.1 | zmieniłby 214 plików; około 80% to zmiany mechaniczne (constructor promotion, str_contains, operator nullsafe) |
Blokery przed PHP 8, w kolejności, w jakiej wyjdą:
app/Libraries/LegacyCsv.phpużywaeach(), usuniętej w PHP 8.0. Z tej klasy korzysta miesięczny import: błąd krytyczny przy pierwszym uruchomieniu po aktualizacji.- 23 klasy używają dynamicznych właściwości (deprecated w PHP 8.2), głównie w
app/Services/Pricing/. - Interpolacja
${…}w stringach (znak dolara przed klamrą) w 31 miejscach (deprecated w PHP 8.2). - Niejawnie nullowalne parametry w 58 sygnaturach metod (deprecated w PHP 8.4).
- Laravel 6 działa najwyżej na PHP 8.0, więc aktualizacje PHP i frameworka trzeba przeplatać (punkt 6).
Hotspoty
Pliki najczęściej zmieniane w ostatnich 24 miesiącach, z ich rozmiarem.
| Plik | Linie | Commity (24 miesiące) | Uwagi |
|---|---|---|---|
app/Http/Controllers/BookingController.php | 2140 | 61 | wyliczanie ceny, walidacja i e-maile w jednej klasie; pierwszy kandydat do testów charakteryzujących |
app/Services/InvoiceService.php | 1310 | 38 | generowanie PDF, numeracja, VAT; numeracja korzysta z MAX(id)+1 bez blokady |
app/Http/Controllers/Api/DriverController.php | 870 | 29 | obsługa JWT napisana ręcznie wokół php-jwt |
4.4 Konfiguracja i bezpieczeństwo
| ID | Ustalenie | Dowód | Zalecenie |
|---|---|---|---|
| C1 | Staging jest publiczny, APP_DEBUG=true, zainstalowany Ignition | staging.acme-fleet.example odpowiada bez uwierzytelnienia | jeszcze dziś ograniczyć dostęp po IP albo HTTP auth; ustawić APP_DEBUG=false; wdrażać z --no-dev |
| C2 | Baza stagingu to cotygodniowa kopia produkcji z danymi osobowymi | zadanie cron refresh-staging.sh | zastąpić kopią zanonimizowaną; to także problem z RODO |
| C3 | Sekrety w historii | gitleaks: .env.production zacommitowany w marcu 2021 i usunięty w późniejszym commicie; zawiera APP_KEY, DB_PASSWORD, MAIL_PASSWORD | zmienić wszystkie trzy; patrz uwaga o APP_KEY poniżej |
| C4 | Zaufane proxy ustawione na * | app/Http/Middleware/TrustProxies.php | wpisać adres load balancera; dziś każdy klient może podać własne IP w X-Forwarded-For, co omija limit prób logowania |
| C5 | Ciasteczko sesji bez flagi Secure, brak HSTS | SESSION_SECURE_COOKIE nieustawione; brak nagłówka Strict-Transport-Security | ustawić oba po potwierdzeniu, że HTTPS działa wszędzie |
| C6 | Zasoby zdalne w dompdf włączone | config/dompdf.php: enable_remote => true | osadzić logo jako plik lokalny i wyłączyć zasoby zdalne |
| C7 | Worker kolejki uruchamiany z crona co minutę, jako root | crontab -l dla roota | uruchamiać przez Supervisor albo systemd jako użytkownik aplikacji |
| C8 | Kopie zapasowe: nocny mysqldump do /var/backups na tym samym serwerze | crontab, listing dysku | kopia poza serwerem, szyfrowanie, comiesięczny test odtworzenia |
| C9 | Weryfikacja JWT przyjmuje algorytm z nagłówka tokenu | DriverController::decodeToken() | obejmie to aktualizacja do php-jwt 6; przy okazji poprawić obsługę tokenów |
O zmianie APP_KEY
Laravel używa tego klucza do szyfrowania ciasteczek, podpisywania adresów URL i do każdej wartości, którą Twój kod szyfruje przez Crypt::encrypt() albo encrypt(). Zmiana klucza wylogowuje wszystkich użytkowników, a te wartości stają się nieczytelne, jeśli nie zostaną zaszyfrowane ponownie. Portal przechowuje jedno takie pole (numery dokumentów tożsamości kierowców, szyfrowane w mutatorze modelu). Etap 0 obejmuje krótki skrypt, który odszyfrowuje dane starym kluczem i szyfruje je nowym.
4.5 Środowisko uruchomieniowe
Serwer produkcyjny działa na Ubuntu 20.04 z pakietem PHP 7.4 z dystrybucji. Standardowe wsparcie bezpieczeństwa Ubuntu 20.04 skończyło się w maju 2025. Serwer nie jest podłączony do Ubuntu Pro, więc ani pakiet PHP, ani reszta systemu operacyjnego nie dostają aktualizacji bezpieczeństwa.
Podłączenie serwera do Ubuntu Pro przywróciłoby je do maja 2030 dla pakietów z głównego repozytorium Ubuntu, w którym jest PHP 7.4. To naprawia tylko środowisko uruchomieniowe. Nie zmienia niczego w Laravelu ani w paczkach z punktu 4.1.
4.6 Co jest w dobrym stanie
- Migracje bazy danych odtwarzają cały schemat od pustej bazy bez błędów, więc powtarzalne środowisko nie wymaga ręcznego SQL.
- Callback płatności weryfikuje podpis operatora, zanim zmieni zamówienie.
- Aplikacja korzysta z wbudowanych w Laravela kolejek, harmonogramu zadań i poczty, bez własnych zamienników, więc ścieżka aktualizacji idzie zgodnie z oficjalnymi przewodnikami.
5. Priorytety
| Priorytet | Co | Dlaczego najpierw |
|---|---|---|
| P0, w tym tygodniu | C1, C2, C3, C6, C8 | da się to wykorzystać już teraz albo uniemożliwiłoby odtworzenie danych po incydencie |
| P0, w tym tygodniu | laravel/framework 6.20.0 → 6.20.45, league/flysystem → 1.1.4, guzzlehttp/guzzle → 6.5.8, guzzlehttp/psr7 → 1.9.1 | wydania poprawkowe w tej samej wersji głównej, niskie ryzyko nawet bez testów |
| P1, etap 1 | testy na sześciu krytycznych ścieżkach, CI, runbook, monitoring, C4, C5, C7, Ubuntu Pro albo nowy serwer | bez tego niczego innego nie da się bezpiecznie zmienić |
| P2, etapy 2–3 | aktualizacja PHP i Laravela, wymiana porzuconych paczek, C9 | wymaga siatki testów z etapu 1 |
6. Plan i stałe ceny
Ceny w tym przykładzie są oparte na naszych opublikowanych cenach wyjściowych. Prawdziwy audyt kończy się taką ofertą jak ta, z cenami stałymi dla określonego w niej zakresu.
Wariant A (rekomendowany): siatka bezpieczeństwa, potem Laravel 13 na PHP 8.4
| Etap | Zakres | Czas | Stała cena | Płatność |
|---|---|---|---|---|
| 0. Ograniczenie ryzyka | pozycje z priorytetem P0 z punktu 5; Twój zespół może je wykonać w 1–2 dni według naszej instrukcji krok po kroku albo zrobimy to sami w pierwszym tygodniu etapu 1 | 1 tydzień | w cenie etapu 1 | razem z etapem 1 |
| 1. Przejęcie aplikacji | środowisko Docker odtwarzające produkcję; CI; testy charakteryzujące dla sześciu krytycznych ścieżek; baseline PHPStan na poziomie 5; kopie zapasowe poza serwerem z testem odtworzenia; monitoring; runbook i mapa architektury; inwentarz dostępów | 4 tygodnie (rozmiar M) | 13 000 zł | 100% przy zamówieniu |
| 2. Aktualizacja I | Laravel 6 → 9 i PHP 7.4 → 8.1 krokami (6 → 8 na PHP 7.4, potem PHP 8.0, potem Laravel 9 na PHP 8.1); SwiftMailer → Symfony Mailer; php-jwt 6 z poprawką obsługi JWT; dompdf 2.x; Guzzle 7; Faker → fakerphp/faker; przepisanie LegacyCsv | 6 tygodni | 26 000 zł | 30% / 70% po zielonym CI i Twojej akceptacji na stagingu |
| 3. Aktualizacja II | Laravel 9 → 13 i PHP 8.1 → 8.4; serwer z Ubuntu 24.04; phpspreadsheet i dompdf do aktualnych wersji; usunięcie fideloper/proxy; usunięte dynamiczne właściwości i deprecations | 6 tygodni | 26 000 zł | 30% / 70% po zielonym CI i Twojej akceptacji na stagingu |
| Care Standard | comiesięczne aktualizacje PHP i zależności, monitoring, 8 godzin na zmiany, reakcja na krytyczne zgłoszenia w ciągu 4 godzin roboczych, kwartalny przegląd | od 5. miesiąca | 3500 zł/mies. | co miesiąc |
Wariant B: utrzymać obecną wersję w bezpiecznym stanie przez 12 miesięcy
Etap 0 i etap 1 jak wyżej (13 000 zł), Ubuntu Pro na obecnym serwerze dla poprawek środowiska uruchomieniowego (subskrypcję opłacasz bezpośrednio w Canonical), wydania poprawkowe z punktu 5 i Care Basic za 1700 zł miesięcznie.
To zamyka oczywiste dziury, zanim zdecydujesz o przyszłości aplikacji. Nie usuwa jednak 5 zgłoszeń dotyczących Laravela, które nie mają poprawki w linii 6.x, problemów z dompdf i phpspreadsheet wymagających aktualizacji do nowych wersji głównych ani blokerów przed PHP 8. Za 12 miesięcy wariant A będzie kosztował mniej więcej tyle co dziś, plus to, co przyniosą nowe zgłoszenia.
Wariant B ma sens, jeśli portal zostanie zastąpiony albo wyłączony w ciągu 18 miesięcy albo jeśli budżet na ten rok jest już zamknięty.
Wariant C: przepisanie od nowa
Nie rekomendujemy. Reguły biznesowe portalu (wyliczanie cen, numeracja faktur, obsługa VAT) istnieją tylko w kodzie i żadna nie jest przetestowana. Przepisanie oznaczałoby odkrywanie ich od nowa, a stary system i tak potrzebowałby wariantu B przez cały czas budowy nowego.
7. Czego potrzebujemy od Ciebie na start
- Decyzji: wariant A albo B.
- Do etapu 0: osoby z dostępem administratora do stagingu i bazy danych, która razem z nami zmieni hasła, w rozmowie albo e-mailem.
- Do etapu 1: imiennych kont dla nas w hostingu Git i na stagingu oraz osoby, która rozstrzygnie, jak ma działać każda z sześciu krytycznych ścieżek, gdy kod i użytkownicy mówią co innego.
Załącznik A: pełny skan zależności. Kompletny wynik composer audit (JSON, wszystkie 114 zgłoszeń) dostajesz razem z raportem jako osobny plik.
Raport i wszystkie ustalenia są poufne i objęte NDA.
Jak tak szczegółowy raport mieści się w cenie
Narzędzia liczą, a kod czytamy my. Narzędzia AI do programowania, używane za Twoją pisemną zgodą, przyspieszają czytanie i sprawdzanie. Każde ustalenie przegląda inżynier, zanim trafi do raportu. Więcej w Jak pracujemy i Bezpieczeństwo i zaufanie.
Chcesz taki raport dla swojej aplikacji?
Code Audit od 3500 zł, raport w 5–7 dni roboczych. Jeszcze nie wiesz? Zacznij od Debt Check za 1300 zł.