Files
2026-09-03 11:24:19 +02:00

12 KiB

Audyt techniczny — SoftSPM

Data: 2026-06-09 Zakres: analiza kodu źródłowego (PHP) aplikacji SoftSPM Środowisko docelowe (niezmienne): PHP 5.4.35, MariaDB 10.6.23, XAMPP/Apache, Windows Bazy danych: softspm (główna), softspm_rep (dane historyczne + tabele raportowe), softspm_his (archiwum z minionych lat)

Uwaga: audyt schematu baz (typy tabel, indeksy, klucze, silnik MyISAM/InnoDB) zostanie uzupełniony po dostarczeniu struktury tabel. Poniżej wyłącznie ustalenia z analizy kodu.


1. Podsumowanie wykonawcze

Aplikacja jest sprawnym, rozbudowanym systemem dziedzinowym (monitoring odwodnienia kopalni: studnie, pompy, silniki, piezometry, raporty, integracja SCADA SYNDIS przez SOAP), ale pod względem inżynierskim to kod podwyższonego ryzyka: brak warstwy dostępu do danych, masowe składanie zapytań SQL ze stringów z danymi użytkownika, słabe uwierzytelnianie i wiele plików operacyjnych/backupów dostępnych z poziomu sieci.

Ograniczenie „PHP 5.4 i tak musi zostać" nie blokuje napraw bezpieczeństwa — mysqli z zapytaniami przygotowanymi oraz mysql_real_escape_string są dostępne już w PHP 5.4. Rekomendacje poniżej są w pełni zgodne z tym środowiskiem.

Obszar Ocena Najważniejszy problem
Bezpieczeństwo 🔴 Krytyczne Powszechny SQL injection, słabe logowanie, LFI w routerze
Architektura 🟠 Wysokie Brak warstw, func.php ~4000 linii, twardo zakodowane nazwy baz
Utrzymywalność 🟠 Wysokie Brak VCS, brak testów, pliki jednorazowe/backupy w repo
Niezawodność/operacje 🟡 Średnie Brak transakcji, echo/debug w ścieżkach produkcyjnych
Zgodność z PHP 5.4 🟢 OK Kod działa, ale ext/mysql jest usunięty w PHP 7+ — blokuje aktualizację

2. Bezpieczeństwo (priorytet krytyczny)

2.1 SQL Injection — powszechny

  • Funkcje-wrappery w func.php (sqlValue, sqlStringValue, sqlStringArray, sqlGetResAsArray) nie wykonują żadnego escapowania — wykonują przekazany string wprost przez mysql_query.
  • W całym kodzie 0 wystąpień mysql_real_escape_string (poza bibliotekami vendor) przy ~2441 użyciach $_GET/$_POST/$_REQUEST i zapytaniach budowanych konkatenacją.
  • Parametry tekstowe trafiają do SQL bez filtrowania, np. w ajax_func.php:
    • ...WHERE DRS_ID=".$_POST['post_dev'] (wartość liczbowa użyta surowo),
    • ...DRS_DEPARTMENT='".$_POST['post_dep']."' (string surowy),
    • ...WLL_NAME='".$_POST['post_well_name']."' (string surowy).
  • getFloatValue()/getFullFloatValue() dają częściową ochronę dla pól liczbowych (usuwają cudzysłowy, rzutują na float), ale są stosowane niekonsekwentnie, a pól tekstowych i identyfikatorów *_dev/*_id nie chronią.

Rekomendacja (zgodna z PHP 5.4):

  1. Przejść z ext/mysql na mysqli z zapytaniami przygotowanymi (prepare/bind_param). Migrację zacząć od ścieżek przyjmujących dane użytkownika (ajax_func.php, ajax_getdata4well.php, wyszukiwarki w sites/*, importery soap_*).
  2. Etap przejściowy, gdy pełna migracja jest niemożliwa od razu: rozszerzyć wrappery o escapowanie i wymusić rzutowanie identyfikatorów na (int) — natychmiast obcina najczęstszy wektor.

2.2 Uwierzytelnianie i sesje

W index.php i usrLoginAndSiteAccess() (func.php:1340):

  • Hasła w MD5 bez soli (USR_PASSWORD = md5($passwd)) — podatne na tablice tęczowe.
  • SSO z SYNDIS oparty o md5(USR_USERNAME) jako token ?auth= — token jest w pełni przewidywalny dla każdego, kto zna nazwę użytkownika (brak sekretu/HMAC, brak czasu ważności).
  • W ścieżce SSO logowanie następuje hasłem username.".123" — sugeruje domyślny, łatwy do odgadnięcia wzorzec haseł.
  • Brak ochrony CSRF na formularzach i akcjach zapisujących (ajax_func.php).
  • Brak session_regenerate_id() po logowaniu (ryzyko session fixation); ciasteczka sesji bez atrybutów HttpOnly/Secure/SameSite.

Rekomendacja: HMAC z sekretem serwerowym + znacznik czasu dla tokenu SSO; wymuszenie zmiany haseł domyślnych; docelowo password_hash/bcrypt (dostępne od PHP 5.5 — przy 5.4 użyć crypt() z solą bcrypt lub biblioteki password_compat); token CSRF w formularzach; session_regenerate_id(true) po zalogowaniu; twarde atrybuty ciasteczka sesyjnego.

2.3 Local File Inclusion w routerze

index.php:342-345 wykonuje include './sites/'.$_GET['pg'].'.php' bez walidacji pg. Pozwala to na directory traversal / dołączenie nieprzewidzianych plików. Rekomendacja: whitelist dozwolonych nazw stron (np. preg_match('/^site[0-9A-Za-z_]+$/') + sprawdzenie is_file), odrzucanie reszty.

2.4 XSS

Wartości z sesji i parametrów są wypisywane bez htmlspecialchars (np. echo $_SESSION['user'], etykiety, parametry wyszukiwarek). Przy słabej kontroli wejścia daje to trwały/refleksyjny XSS. Rekomendacja: escapować całe wyjście HTML (htmlspecialchars(..., ENT_QUOTES, 'UTF-8')).

2.5 Sekrety i pliki dostępne z sieci

  • dbaccess.php zawiera login i hasło do bazy w repozytorium, plus zakomentowane dane produkcyjne (host 91.196.51.170, użytkownik softspm, hash hasła). Należy usunąć z repo i przenieść do konfiguracji poza webrootem.
  • W webroocie leżą i są pobieralne: error.log (~170 KB), custom_log, katalog sites/backup/, liczne pliki jednorazowe/diagnostyczne (test.php, test_dat.php, naprawa_20210312.php, repair_20210313_data.php, rollback.php, *_soap.php, soap_aaa.php). To wyciek informacji i dodatkowa powierzchnia ataku.
  • Endpointy soap_*.php i ajax_* — zweryfikować, czy wymagają zalogowania; importery SYNDIS z twardo wpisanymi adresami IP nie powinny być wywoływalne anonimowo.

Rekomendacja: przenieść logi i konfigurację poza katalog publiczny; usunąć/zablokować pliki diagnostyczne i sites/backup; dodać reguły dostępu (.htaccess/konfiguracja Apache) na pliki nie będące punktami wejścia.


3. Architektura i utrzymywalność

  • Brak systemu kontroli wersji w katalogu projektu (to nie repozytorium git). Historia zmian żyje w nazwach plików (*_20180426.php, backup/, naprawa_*, daty w nazwach). To poważne ryzyko operacyjne. Rekomendacja: założyć repozytorium git i wciągnąć stan bazowy.
  • Brak warstw: logika dostępu do danych, reguły biznesowe i HTML są wymieszane w plikach sites/* i w monolitycznym func.php (~4000 linii, ~140 funkcji). Utrudnia testowanie i zmiany.
  • Twardo zakodowane nazwy baz softspm_rep. i softspm_his. w zapytaniach (m.in. func.php, zapytania UNION/podzapytania łączące bazy). Każda zmiana nazwy/serwera bazy wymaga przeszukania całego kodu. Rekomendacja: wprowadzić stałe konfiguracyjne (np. define('DB_REP', 'softspm_rep')) i składać nazwy z nich.
  • Duplikacja i martwy kod: wiele wariantów raportów (rep_002_*_print.php per miesiąc), soap_of_while* w pięciu wersjach, foldery backup/. Utrudnia ustalenie, co jest „żywe".
  • Vendored biblioteki przestarzałe i bez menedżera zależności: PHPExcel (projekt zarchiwizowany, następca PhpSpreadsheet), tcpdf, fpdf, pChart, ckEditor, PHPWord. Brak ścieżki łatania podatności. Rekomendacja: spisać wersje, ocenić znane CVE, rozważyć kontrolowaną aktualizację tych zgodnych z PHP 5.4.
  • Brak testów automatycznych — każda zmiana w func.php (używanym niemal wszędzie) jest ryzykowna. Rekomendacja: zacząć od testów charakteryzujących krytyczne funkcje obliczeniowe (wellEvaluation4_*, getGlobalPumpedWater*, liczCzasu).

4. Baza danych i dane (wstępnie, do uzupełnienia po schemacie)

  • Sharding miesięczny tabel historycznych w kodzie: nazwy budowane dynamicznie (getTableNameFrom_everyday_his, getTableNameFrom_resources_det, sufiksy rep_his_data_everyday_, seg_dev_resources_det_). Ryzyko: zapytania do nieistniejącej tabeli dla brakującego okresu; trudna ewolucja schematu. Zweryfikować obsługę braku tabeli.
  • Zapytania cross-database (JOIN/UNION między softspm, softspm_rep, softspm_his, np. func.php:1729). Wymaga, by wszystkie trzy bazy były na jednym serwerze; utrudnia rozdzielenie/skalowanie. Zwrócić uwagę na wydajność takich UNION po dużych tabelach archiwum.
  • Operacje masowe bez transakcji: TRUNCATE + INSERT ... SELECT (np. odświeżanie rep_object_departments w ajax_func.php:19). Przy błędzie w połowie tabela zostaje pusta/niespójna. Rekomendacja: transakcje (wymaga InnoDB) lub budowanie do tabeli tymczasowej i RENAME.
  • Do sprawdzenia po dostarczeniu schematu: silnik tabel (MyISAM vs InnoDB — kluczowe dla transakcji i blokad), indeksy pod najczęstsze filtry (DRD_DRS_ID, DRD_RESOURCES_DETAIL, DRD_AUDIT_DATE), typy kolumn dat, spójność kluczy obcych między bazami, kodowanie znaków (kod ustawia utf8/utf8_general_ci).

5. Niezawodność i operacje

  • Debug/echo w ścieżkach produkcyjnych: error_reporting(E_ALL) i echo 'starting...' w soap_update5min.php (zadanie cykliczne co 5 min); komunikaty mysql_error() zwracane do wyjścia w sqlGetResAsArray. Ujawnia strukturę i zaśmieca logi.
  • Brak obsługi błędów połączenia poza or die('cannot connect to db') — brak kontrolowanej degradacji.
  • Zadania cykliczne przez .bat + php-cli (reports/rep_003_hours.bat, poll SYNDIS) z twardo wpisanymi ścieżkami i adresami IP — kruche przy zmianie środowiska; brak centralnej konfiguracji i monitoringu wykonania.
  • mysql_* (ext/mysql) jest usunięty w PHP 7+ — to twarda blokada jakiejkolwiek przyszłej aktualizacji PHP. Nawet jeśli teraz pozostajemy na 5.4, migracja do mysqli zdejmuje tę blokadę „przy okazji" poprawek bezpieczeństwa.

6. Plan działania (priorytetyzacja)

P0 — natychmiast (bezpieczeństwo, niskie ryzyko regresji):

  1. Whitelist pg w index.php (usunięcie LFI).
  2. Zablokować dostęp HTTP do error.log, custom_log, sites/backup/, plików test*/naprawa*/repair*/rollback*/*_soap.php (konfiguracja Apache/.htaccess).
  3. Przenieść dbaccess.php poza webroot i usunąć zakomentowane dane produkcyjne.
  4. Rzutować wszystkie identyfikatory z $_GET/$_POST na (int) w endpointach zapisujących.

P1 — krótki termin: 5. Migracja ext/mysql → mysqli z prepared statements, zaczynając od ajax_func.php, ajax_getdata4well.php, importerów soap_* i wyszukiwarek. 6. Token SSO oparty o HMAC+czas; wymuszenie zmiany haseł domyślnych; session_regenerate_id i bezpieczne atrybuty ciasteczek; tokeny CSRF. 7. Escapowanie całego wyjścia HTML (XSS). 8. Założenie repozytorium git i odcięcie martwego kodu/backupów.

P2 — średni termin: 9. Konfiguracyjne stałe nazw baz; wyłączenie debug/echo z produkcji. 10. Transakcje dla operacji masowych (po potwierdzeniu InnoDB). 11. Testy charakteryzujące krytyczne funkcje obliczeniowe. 12. Inwentaryzacja i ocena CVE bibliotek vendor.


7. Mocne strony (do zachowania)

  • Spójna konwencja nazewnicza tabel/kolumn (seg_/sys_/rep_/tst_, prefiksy kolumn) i stron (siteNNN + sufiksy) — łatwo się odnaleźć po opanowaniu wzorca.
  • Wydzielone wrappery SQL i słownikowe — dobry punkt zaczepienia do wprowadzenia bezpiecznej warstwy dostępu (zmiana w jednym miejscu obejmie wiele wywołań).
  • Czytelny rozdział ról SOAP (klient SYNDIS vs serwer webservice/).