# 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](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](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](index.php) i `usrLoginAndSiteAccess()` ([func.php:1340](func.php#L1340)): - **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](index.php#L342) 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](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](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](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](func.php#L1729)). 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](ajax_func.php#L19)). 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](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/`).