Audyt aplikacji PHP zamawia się zwykle wtedy, gdy trzeba podjąć ważną decyzję: przejąć system od innego wykonawcy, zaplanować modernizację, przygotować produkt do wzrostu, ograniczyć liczbę awarii albo ocenić ryzyko przed inwestycją. W każdym z tych przypadków samo stwierdzenie, że „kod wymaga refaktoryzacji”, jest praktycznie bezużyteczne.

Profesjonalny audyt powinien odpowiedzieć na cztery pytania: co jest problemem, dlaczego ma znaczenie, co należy z tym zrobić i w jakiej kolejności. Jego jakość mierzy się nie liczbą stron raportu, lecz tym, czy po spotkaniu podsumowującym zarząd i zespół techniczny potrafią podjąć konkretne decyzje.

1. Cel, zakres i ograniczenia audytu

Ten sam fragment kodu może być akceptowalny w rzadko używanym panelu wewnętrznym i niedopuszczalny w procesie obsługującym płatności. Dlatego audyt powinien zaczynać się od rozmowy o systemie, a nie od uruchomienia narzędzi. Audytor musi wiedzieć, jaką decyzję ma wesprzeć analiza i które procesy biznesowe są krytyczne.

Przed rozpoczęciem prac warto jednoznacznie ustalić:

  • repozytoria, usługi, integracje i środowiska objęte analizą,
  • obszary wyłączone oraz powód ich wyłączenia,
  • czy badany jest cały system, czy reprezentatywna próbka,
  • dostęp do kodu, konfiguracji, logów, metryk i procesu wdrażania,
  • krytyczne ścieżki użytkownika oraz konsekwencje ich awarii,
  • oczekiwane artefakty, sposób oceny ryzyka i termin prezentacji wyników.
Ograniczenia są częścią wyniku

Jeżeli audyt nie obejmował produkcji, bazy danych albo testów penetracyjnych, raport powinien mówić o tym wprost. Brak dostępu nie może zmienić się w pozorną pewność.

2. Inwentaryzacja systemu i cyklu życia technologii

Zanim oceni się jakość rozwiązania, trzeba zrozumieć, z czego naprawdę się składa. Dokumentacja często pokazuje stan sprzed kilku lat, a istotna logika może działać w zadaniach cyklicznych, workerach kolejek lub skryptach, których nie widać w głównym repozytorium.

Inwentaryzacja powinna objąć co najmniej:

  • wersję PHP, frameworka, rozszerzeń oraz bibliotek zarządzanych przez Composer,
  • serwer HTTP, PHP-FPM, system bazowy, obrazy kontenerów i konfigurację runtime'u,
  • bazy danych, cache, kolejki, workery, harmonogramy zadań i magazyny plików,
  • publiczne API, webhooki, systemy zewnętrzne i przepływy danych,
  • środowiska, konfigurację, sekrety oraz sposób budowania i wdrażania,
  • właściciela każdego ważnego komponentu i zależności od dostawców.

Wynikiem powinien być aktualny diagram systemu, lista integracji oraz macierz technologii pokazująca stan wsparcia, przeszkody w aktualizacji i zależności między zmianami. Samo zalecenie „zaktualizować wszystko” nie jest planem, potrzebna jest kolejność, sposób weryfikacji i możliwość wycofania wdrożenia.

3. Architektura i jakość kodu PHP

Analiza automatyczna przyspiesza wyszukiwanie problemów, ale nie wyjaśnia, czy kod dobrze odzwierciedla procesy biznesowe. Statyczna analiza, kontrola standardów, skan zależności i metryki złożoności powinny wspierać ręczny przegląd obszarów o największym ryzyku, nie zastępować go.

W tej części audytu należy sprawdzić między innymi:

  • granice modułów, odpowiedzialności klas i kierunek zależności,
  • sprzężenie logiki biznesowej z frameworkiem, bazą oraz transportem HTTP,
  • duplikację reguł biznesowych, globalny stan i cykliczne zależności,
  • obsługę błędów, wyjątków, typowanie i kontrakty między komponentami,
  • hotspoty łączące wysoką złożoność z częstymi zmianami lub incydentami,
  • kod martwy, historyczne obejścia i bariery blokujące aktualizację platformy,
  • możliwość izolowanego testowania najważniejszych przypadków użycia.

Najbardziej użytecznym artefaktem nie jest ogólna ocena „architektura 6/10”, lecz mapa hotspotów z przykładami plików, klas i zależności oraz wyjaśnieniem, jaki skutek mają one dla wdrożeń, stabilności lub kosztu zmian.

4. Bezpieczeństwo, integralność danych i prywatność

Bezpieczeństwo powinno być oceniane w kontekście zasobów, granic zaufania i realnych scenariuszy nadużycia. Checklista popularnych podatności jest dobrym punktem startu, ale nie wykryje na przykład sytuacji, w której poprawnie zalogowany użytkownik może odczytać cudzą fakturę przez zmianę identyfikatora.

Zakres analizy powinien obejmować:

  • uwierzytelnianie, autoryzację oraz kontrolę dostępu do konkretnych obiektów,
  • sesje, cookies, ochronę żądań, konfigurację CORS i limity operacji,
  • walidację wejścia, zapytania do bazy, szablony, pliki i wywołania systemowe,
  • przechowywanie oraz rotację haseł, kluczy, tokenów i innych sekretów,
  • podatne lub porzucone zależności i rozszerzenia środowiska,
  • dane wrażliwe w logach, retencję, szyfrowanie i uprawnienia do danych,
  • model bazy, ograniczenia, indeksy, transakcje, migracje i ryzyko wyścigów,
  • kopie zapasowe oraz dowód, że procedura odtworzenia rzeczywiście działa.
Audyt kodu to nie pentest

Przegląd bezpieczeństwa w kodzie, skan podatności i test penetracyjny odpowiadają na różne pytania. W systemach wysokiego ryzyka powinny się uzupełniać, a raport musi jasno nazywać wykonaną metodę.

5. Wydajność, skalowalność i odporność na awarie

Rekomendacje wydajnościowe bez pomiarów są zgadywaniem. Audyt powinien rozpocząć się od danych z ruchu reprezentatywnego dla aplikacji: czasu odpowiedzi, liczby błędów, zużycia zasobów, wolnych zapytań, opóźnień integracji i długości kolejek. Dopiero później można oceniać sens cache, zmian w bazie lub skalowania poziomego.

Poza typowymi wąskimi gardłami trzeba sprawdzić zachowanie systemu podczas częściowej awarii:

  • timeouty i retry dla usług zewnętrznych,
  • idempotencję operacji ponawianych przez użytkownika lub kolejkę,
  • obsługę nieprzetworzonych wiadomości i możliwość ich bezpiecznego wznowienia,
  • pojedyncze punkty awarii oraz zależność od współdzielonych zasobów,
  • konfigurację workerów, OPcache, PHP-FPM i limitów pamięci,
  • cele odtworzenia systemu i dopuszczalnej utraty danych.

Raport powinien zawierać metodę pomiaru, wynik bazowy i mierzalny cel po zmianie. Informacja „zapytanie jest wolne” ma znacznie mniejszą wartość niż wskazanie konkretnej ścieżki, czasu, planu wykonania i wpływu na proces.

6. Testy i proces dostarczania zmian

Procent pokrycia kodu nie mówi, czy aplikacja jest bezpieczna w rozwoju. Ważniejsze jest to, czy testy chronią procesy, których awaria ma realny koszt: rozliczenia, uprawnienia, migracje danych, integracje i publiczne API. Audyt powinien zmapować krytyczne zachowania na testy jednostkowe, integracyjne, kontraktowe i end-to-end.

Równolegle trzeba przeanalizować drogę zmiany od komputera programisty do produkcji:

  • powtarzalność lokalnego środowiska oraz builda,
  • czas, stabilność i zakres testów uruchamianych w CI,
  • statyczną analizę, skan zależności i inne bramki jakości,
  • code review, zasady łączenia zmian i zarządzanie plikiem lock,
  • wdrażanie migracji bazy oraz zgodność zmian wstecz,
  • strategie wdrożenia, feature flagi, rollback i weryfikację po publikacji,
  • różnice konfiguracji między środowiskami.

W tym obszarze często znajdują się szybkie usprawnienia o dużym zwrocie: automatyczny smoke test, zablokowanie wdrożenia przy krytycznym błędzie albo udokumentowana procedura rollbacku może ograniczyć ryzyko wcześniej niż wielomiesięczna refaktoryzacja.

7. Obserwowalność, utrzymanie i ryzyka organizacyjne

System może mieć poprawny kod, a mimo to być trudny w utrzymaniu. Audyt powinien sprawdzić, czy zespół potrafi szybko zauważyć problem, ustalić jego przyczynę i przywrócić usługę. Obejmuje to logowanie strukturalne, identyfikatory korelacyjne, metryki, alerty, monitoring kolejek i zadań cyklicznych, health checki oraz runbooki.

Istotna jest również koncentracja wiedzy. Audyt nie służy ocenie ludzi, ale powinien wskazać komponenty znane tylko jednej osobie, brak właściciela, nieaktualną dokumentację, ręczne operacje i zależność od pojedynczego dostawcy. To ryzyka systemu tak samo realne jak niewspierana biblioteka.

Dobrym rezultatem tej części jest mapa odpowiedzialności, lista luk w gotowości operacyjnej oraz scenariusze reakcji na najważniejsze awarie, wraz z informacją, czy zespół kiedykolwiek przećwiczył odtworzenie systemu.

8. Jak powinno wyglądać pojedyncze ustalenie audytowe

Lista haseł typu „brak testów”, „duże klasy” albo „stary framework” przenosi całą pracę interpretacyjną na zamawiającego. Każde ustalenie powinno być samodzielnym opisem decyzji: zawierać dowód, warunki wystąpienia, wpływ, rekomendację, zależności i sposób ponownej weryfikacji.

finding-SEC-03.txt
ID:             SEC-03
Obserwacja:     brak kontroli właściciela dokumentu
Dowód:          endpoint GET /documents/{id}
Scenariusz:     użytkownik zmienia identyfikator zasobu
Wpływ:          ujawnienie danych innego klienta
Priorytet:      wysoki — duży wpływ, proste nadużycie
Rekomendacja:   polityka dostępu na poziomie obiektu
Weryfikacja:    test integracyjny dla dostępu między kontami

Priorytet nie powinien wynikać wyłącznie z technicznej kategorii błędu. Trzeba uwzględnić prawdopodobieństwo, wpływ biznesowy, zasięg, możliwość wykrycia awarii, koszt naprawy i zależności. Dzięki temu krytyczne ryzyko utraty danych nie ginie obok kilkudziesięciu uwag o stylu kodu.

9. Raport, priorytety i realistyczna roadmapa

Wynik audytu musi działać na dwóch poziomach. Osoba odpowiedzialna za budżet potrzebuje krótkiego podsumowania ryzyk, konsekwencji i wariantów działania. Zespół techniczny potrzebuje dowodów, lokalizacji problemów, rekomendacji i kryteriów akceptacji. Jedna lista bez rozróżnienia odbiorców zwykle nie spełnia żadnej z tych potrzeb.

Minimalny zestaw materiałów końcowych

  • Streszczenie zarządcze opisujące najważniejsze ryzyka i decyzje.
  • Szczegółowy raport techniczny z dowodami oraz ograniczeniami analizy.
  • Rejestr ustaleń z priorytetem, wpływem i sposobem weryfikacji.
  • Aktualny diagram architektury, integracji i przepływu danych.
  • Inwentaryzacja runtime'u, zależności i cyklu ich wsparcia.
  • Quick wins oddzielone od zmian strategicznych.
  • Roadmapa z zależnościami, przedziałem wysiłku i właścicielem działania.
  • Warsztat podsumowujący oraz możliwość wyjaśnienia ustaleń zespołowi.

Praktyczna roadmapa może dzielić działania na natychmiastowe zabezpieczenia, najbliższe tygodnie, kolejny kwartał i zmiany strategiczne. Powinna pokazać quick wins, ale też kolejność wynikającą z zależności, na przykład najpierw testy krytycznego procesu, potem aktualizacja platformy, a dopiero później głębsza zmiana architektury.

Czego nie powinien robić dobry audyt

Nie powinien ograniczać się do raportu ze skanera, przyznawać arbitralnej oceny „63/100”, szukać winnych ani zalecać rewrite'u tylko dlatego, że system jest stary. Rekomendacja bez dowodu, wpływu i kolejnego kroku pozostaje opinią.

Jak przygotować się do audytu aplikacji PHP

Sprawny audyt wymaga nie tylko dostępu do repozytorium. Przed rozpoczęciem warto przygotować opis krytycznych procesów, diagramy i instrukcje wdrożenia, listę środowisk, historię incydentów, metryki produkcyjne oraz osoby, które znają produkt, infrastrukturę i wsparcie użytkowników. Niekompletna dokumentacja nie blokuje analizy, ale sama w sobie jest ważnym ustaleniem.

Zespół powinien mieć możliwość skomentowania ustaleń przed finalizacją raportu. Nie po to, by usuwać niewygodne wnioski, lecz żeby wyeliminować błędy faktyczne i poznać kontekst historycznych decyzji. Krytyczny problem nie powinien po raz pierwszy pojawić się dopiero w końcowym pliku PDF.

Audyt ma kończyć się decyzją

Dobry audyt nie kończy się listą błędów. Kończy się wspólną odpowiedzią na pytanie, co zrobić teraz, co zaplanować później i czego świadomie nie zmieniać. To właśnie odróżnia dokument, który trafia do archiwum, od narzędzia ograniczającego ryzyko i porządkującego inwestycje w produkt.

Jeżeli wynik analizy wskazuje na potrzebę większych zmian, kolejnym krokiem powinien być etapowy plan. Zobacz również, jak podejść do modernizacji aplikacji legacy bez big-bang rewrite'u.

Potrzebujesz konkretów?

Sprawdźmy, gdzie naprawdę leży ryzyko.

Audyt zakończymy priorytetami i planem działań, który można przełożyć na decyzje biznesowe oraz zadania zespołu.

Porozmawiajmy o audycie
← Wróć do wszystkich wpisów