126 lines
12 KiB
Markdown
126 lines
12 KiB
Markdown
# 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/`).
|