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 przezmysql_query. - W całym kodzie 0 wystąpień
mysql_real_escape_string(poza bibliotekami vendor) przy ~2441 użyciach$_GET/$_POST/$_REQUESTi 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/*_idnie chronią.
Rekomendacja (zgodna z PHP 5.4):
- Przejść z
ext/mysqlnamysqliz zapytaniami przygotowanymi (prepare/bind_param). Migrację zacząć od ścieżek przyjmujących dane użytkownika (ajax_func.php,ajax_getdata4well.php, wyszukiwarki wsites/*, importerysoap_*). - 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ówHttpOnly/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żytkowniksoftspm, 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, katalogsites/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_*.phpiajax_*— 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.isoftspm_his.w zapytaniach (m.in. func.php, zapytaniaUNION/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.phpper miesiąc),soap_of_while*w pięciu wersjach, folderybackup/. 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, sufiksyrep_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/UNIONmiędzysoftspm,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ść takichUNIONpo dużych tabelach archiwum. - Operacje masowe bez transakcji:
TRUNCATE+INSERT ... SELECT(np. odświeżanierep_object_departmentsw ajax_func.php:19). Przy błędzie w połowie tabela zostaje pusta/niespójna. Rekomendacja: transakcje (wymaga InnoDB) lub budowanie do tabeli tymczasowej iRENAME. - 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 ustawiautf8/utf8_general_ci).
5. Niezawodność i operacje
- Debug/echo w ścieżkach produkcyjnych:
error_reporting(E_ALL)iecho 'starting...'wsoap_update5min.php(zadanie cykliczne co 5 min); komunikatymysql_error()zwracane do wyjścia wsqlGetResAsArray. 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 domysqlizdejmuje tę blokadę „przy okazji" poprawek bezpieczeństwa.
6. Plan działania (priorytetyzacja)
P0 — natychmiast (bezpieczeństwo, niskie ryzyko regresji):
- Whitelist
pgw index.php (usunięcie LFI). - Zablokować dostęp HTTP do
error.log,custom_log,sites/backup/, plikówtest*/naprawa*/repair*/rollback*/*_soap.php(konfiguracja Apache/.htaccess). - Przenieść
dbaccess.phppoza webroot i usunąć zakomentowane dane produkcyjne. - Rzutować wszystkie identyfikatory z
$_GET/$_POSTna(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/).