Laboratorium SQL Injection, Command Injection, File Upload, XSS Reflected oraz XSS Stored.
Dostępne są dwa tryby: publiczny web (domyślny) i pełny local (jawnie ustawiony
w Docker Compose). Parametry URL, formularze i cookies nie zmieniają trybu.
Nieprawidłowy APP_MODE zatrzymuje aplikację.
Nie publikuj repozytorium ani pełnej wersji lokalnej na hostingu współdzielonym. Pełna wersja zawiera celowe wykonanie poleceń, SQL Injection i dowolny upload. Publiczna wersja nadal zawiera rzeczywisty, nieizolowany XSS — przeznacz ją na osobną domenę bez innych aplikacji, kont produkcyjnych i danych poufnych.
| Ćwiczenie | web |
local |
|---|---|---|
| SQL Injection | Symulacja w przeglądarce | Rzeczywiste SQL |
| Command Injection | Symulacja w przeglądarce | Rzeczywiste polecenia |
| Upload | Metadane lokalnego pliku, bez wysyłania | Rzeczywisty upload |
| XSS Reflected / Stored | Rzeczywisty XSS po ostrzeżeniu | Rzeczywisty XSS |
Symulacje obsługują ograniczone przykłady z lekcji i nie wykonują danych wejściowych.
SQL używa fikcyjnych kont admin/admin, test/test, user/user w pamięci strony;
rejestracja dodaje fikcyjne konto do czasu odświeżenia. Komendy obsługują
127.0.0.1, 127.0.0.1; whoami i 127.0.0.1; ls. Upload analizuje wyłącznie nazwę,
rozmiar i deklarowany typ — nie potwierdza bezpiecznej zawartości.
Bez JavaScript dostępne są materiały, ale nie symulacje.
Budowanie pakietów po kompilacji frontendu (Python 3):
npm run build
python3 bin/package-release.pydist/vulnerability-vault-web.zip: pakiet PHP pozbawiony lokalnych backendów, modeli kont i narzędzi administracji lokalnej; zawiera katalog dokumentówpublic. Na osobnej domenie działa bezpośrednio pod/(PL) i/en/(EN).dist/vulnerability-vault-local.zip: pełne źródła i stack Docker, bez sekretów, danych użytkownika,.git,vendorinode_modules.manifest.jsonw każdym pakiecie zawiera sumy SHA-256 plików.
Instalacja publiczna, baza z ograniczonym kontem i cron: deployment/README.md.
Publiczny pakiet nie pozwala włączyć local, nawet zmienną środowiskową.
Należy wgrać pakiet do nowego katalogu wydania, nie nakładać go na pełną instalację.
Web wymaga jawnych danych połączenia; root ani puste dane nie są akceptowane.
Konfiguracja z config.php jest poza WWW; środowisko ma pierwszeństwo.
Wymagane są również APP_ORIGIN (kanoniczny adres HTTPS bez ścieżki) oraz
SESSION_SAVE_PATH (prywatny, zapisywalny katalog poza wszystkimi katalogami WWW,
z prawami 0700). Obce hosty otrzymują 421. Cookie sesji zawsze ma Secure,
HttpOnly i SameSite=Lax; strony informacyjne i symulacje nie tworzą sesji.
Wdrożenie wymaga limitów żądań/równoległości przed PHP, rotacji logów oraz
sprzątania sesji zgodnie z instrukcją instalacji. Sam ZIP nie włącza tych ustawień
u operatora. Wspólny XSS nadal jest niebezpieczny dla odwiedzających.
Stored XSS przyjmuje do 200 wspólnych komentarzy, nazwę do 128 znaków i treść do
4 KiB, z globalnym limitem jednego zapisu na 5 sekund. Komentarze są czyszczone
codziennie o 23:00 UTC przez cron, z nadrabianiem resetu przed odczytem i zapisem.
Blokada transakcyjna InnoDB zapewnia spójność przy równoległych żądaniach.
Reset nie chroni odwiedzających przed kodem wykonanym wcześniej.
Komunikaty sesji są scalane i ograniczone do 10 najnowszych. Oczekiwanie na
blokadę InnoDB trwa maksymalnie 2 s; przejściowe przeciążenie zwraca 503 z
Retry-After: 5. Awaryjne usunięcie wszystkich komentarzy, wyłącznie przez CLI:
php bin/purge-comments.php --confirm (szczegóły w instrukcji instalacji).
Wymagany Docker Engine z Docker Compose v2 lub Docker Desktop. Na Windows włącz integrację Docker Desktop z używaną dystrybucją WSL. W katalogu projektu uruchom:
docker compose up --build -dPierwsze uruchomienie pobiera obrazy i zależności, buduje PHP oraz tworzy bazę. PHP, Composer i Node.js nie muszą być zainstalowane na komputerze.
- Aplikacja: http://localhost:8080/vulnerability-vault/
- phpMyAdmin: http://localhost:8081
- Konta aplikacji:
admin/admin,test/test,user/user. - Domyślne logowanie do phpMyAdmin:
vault/vault-local. - Administrator bazy w phpMyAdmin:
root/root-local.
Compose ogranicza aplikację do 1 CPU / 256 MiB / 128 procesów, bazę do 1 CPU / 512 MiB / 128 procesów, a phpMyAdmin do 0,5 CPU / 256 MiB / 128 procesów. Port bazy nie jest publikowany; kontenery nie montują katalogów hosta ani gniazda Dockera. Wolumeny zachowują dane, ale nie mają limitu rozmiaru — monitoruj miejsce na dysku i resetuj laboratorium. Uruchamiaj je tylko lokalnie. Do eksperymentów z nieznanym kodem użyj osobnej maszyny wirtualnej; Docker współdzieli jądro hosta.
Stack: Apache z PHP 8.2, MariaDB 10.11 oraz phpMyAdmin 5.2.
Kod i Composer dependencies znajdują się w obrazie. Istniejące zasoby frontendowe
nie wymagają kompilacji. Część bibliotek i fontów jest pobierana przez przeglądarkę
z CDN, więc pełny wygląd strony wymaga dostępu do Internetu.
Highlight.js 11.12.0 i jego CSS są dołączone lokalnie; metadane, licencja i sumy
znajdują się w deployment/highlight.json i deployment/highlight-LICENSE.
npm run check:vendor i pakowanie sprawdzają wersje i SHA-256; npm audit
sprawdza przypięte zależności. Publiczne strony poza XSS mają CSP ograniczającą
skrypty do własnej domeny i nonce bootstrapu; XSS zachowuje celowy wyjątek.
Opcjonalnie skopiuj .env.example do .env i ustaw DB_USERNAME, DB_PASSWORD
oraz DB_ROOT_PASSWORD przed pierwszym uruchomieniem. Compose przekazuje
aplikacji DB_HOST=db, DB_PORT=3306, DB_DATABASE=vulnerability-vault.
Użytkownik aplikacji ma uprawnienia wyłącznie do tej bazy; port bazy nie jest
publikowany na hoście.
Zmiana haseł w .env nie zmienia kont w już istniejącym wolumenie MariaDB.
Dla istniejących danych zmień hasła także w bazie, a następnie odtwórz kontenery.
# Stan i logi
docker compose ps
docker compose logs -f app db
# Przebudowanie po zmianach kodu
docker compose up --build -d
# Zatrzymanie i ponowne uruchomienie
docker compose down
docker compose up -d
# Ponowna, idempotentna inicjalizacja tabel i kont testowych
docker compose exec app php bin/init-db.phpBaza jest zapisywana w wolumenie db_data, a pliki ćwiczenia File Upload w
uploads, zamontowanym jako web/uploads. Restart i odtworzenie kontenerów
zachowują te dane. Inicjalizacja dodaje konta testowe tylko wtedy, gdy brakuje
użytkownika o danej nazwie; nie nadpisuje haseł istniejących użytkowników.
Pełny reset laboratorium usuwa bazę i przesłane pliki:
docker compose down -v
docker compose up --build -dGaleria, MongoDB oraz niedokończone ćwiczenie CSRF nie są odtwarzane w tym stacku.
Na Linuksie przykłady Command Injection używają ping -c 4 -W 2 i poleceń powłoki
linuksowej; na Windows kod zachowuje ping -n 4.
Bez Dockera ustaw APP_MODE=local w środowisku PHP/CLI lub config.php.
Następnie umieść projekt w htdocs/vulnerability-vault, zainstaluj zależności
przez composer install, uruchom Apache z mod_rewrite i bazę SQL. Wymagane
rozszerzenia PHP to PDO MySQL i GD z JPEG/FreeType. Przed pierwszym wejściem
uruchom w katalogu projektu:
php bin/init-db.phpPołączenie domyślnie używa localhost:3306, bazy vulnerability-vault, konta
root i pustego hasła. Inne dane można przekazać zmiennymi środowiskowymi
DB_HOST, DB_PORT, DB_DATABASE, DB_USERNAME, DB_PASSWORD; skrypt PHP
nie odczytuje samodzielnie pliku .env. Użytkownik wykonujący inicjalizację musi
mieć uprawnienia do tworzenia tej bazy i tabel. Na Windows utwórz web/uploads
z prawem zapisu dla Apache. Aplikacja jest pod /vulnerability-vault/.
Dane z WAMP/XAMPP nie są importowane automatycznie. W dotychczasowym phpMyAdmin
wyeksportuj bazę vulnerability-vault jako SQL z tabelami i danymi. W dockerowym
phpMyAdmin wybierz tę bazę i zaimportuj eksport. Jeśli eksport zawiera CREATE TABLE, użyj opcji eksportu dodającej DROP TABLE, aby zastąpić tabele utworzone
przy starcie. Import zastępujący tabele usuwa ich dotychczasowe dane — wcześniej
wykonaj eksport, jeśli chcesz je zachować. Po imporcie uruchom ponownie
php bin/init-db.php w kontenerze, aby uzupełnić brakujące konta testowe.
docker compose config --quiet
docker compose exec app sh -c 'find app views routes web bin -name "*.php" -exec php -l {} \;'
docker compose exec app composer check-platform-reqs --no-dev
python3 tests/docker_smoke.pyTest integracyjny wymaga Pythona 3 i działającego Dockera. Buduje osobny projekt Compose na wolnych lokalnych portach, sprawdza ćwiczenia, sesje, zasoby, inicjalizację, trwałość danych i błąd startu przy złym haśle, po czym usuwa wyłącznie własne kontenery, zachowując wolumeny testowe. Sprawdza HTML wariantów XSS; wykonanie JavaScript i wygląd strony należy dodatkowo sprawdzić w przeglądarce.
Interfejs korzysta z istniejących widoków PHP, SCSS i TypeScript. Style systemu
BREACH//LAB znajdują się w web/resources/scss/components/_breach.scss, a katalog
pięciu laboratoriów w views/layouts/components/lab-catalog.php. Backend i pola
formularzy ćwiczeń pozostają zachowane.
Po zmianach źródeł frontendowych (Node.js 20.11+):
npm ci
npm run check
npm run buildWygenerowane CSS, mapy i JS są wersjonowane, więc uruchomienie przez Docker
nie wymaga Node.js. Po zmianach przebuduj aplikację: docker compose up --build -d.
Animacje respektują prefers-reduced-motion; treść i formularze są dostępne
również bez animacji. Interfejs dodaje obsługiwane klawiaturą zakładki porównania
kodu, kopiowanie, numery linii, mobilne menu i nawigację po etapach.
Stopka zawiera indeks laboratoriów, FAQ i odnośniki do projektu oraz autora. FAQ dotyczy uruchomienia i korzystania z laboratorium. Usunięto dawną stronę inwestycyjną, nieużywany slider, popupy oraz licznik czasu. Frontend korzysta z natywnego JavaScript; nie ładuje jQuery, jQuery UI, Poppera ani Bootstrap JS.
Publiczna wersja na osobnej domenie używa /sql-injection (PL) i /en/sql-injection (EN).
Instalacja lokalna zachowuje /vulnerability-vault/sql-injection oraz
/vulnerability-vault/en/sql-injection.
Aliasy strony głównej (/index, /home, /home/index) działają w obu językach.
Język wynika wyłącznie z adresu — bez automatycznego wykrywania, cookies preferencji ani localStorage.
Przełącznik PL / EN w nawigacji działa również bez JavaScript i na telefonach.
Otwiera odpowiednik bieżącej strony z nowym formularzem, bez parametrów testu i bez ponawiania POST.
Adresy akcji, np. /file-uploader/upload, prowadzą po przełączeniu do właściwego ćwiczenia.
JavaScript zachowuje kotwicę bieżącej sekcji. CSS, JS, obrazy i uploady zachowują wspólne adresy.
- Słowniki to
resources/lang/pl.jsoniresources/lang/en.json. Mają identyczne klucze i parametry{name}. TranslationServiceodczytuje język, kontekst trasy i słownik;t('key', ['name' => $value])zwraca tekst, ate(...)escapuje go do HTML. Brak klucza lub parametru powoduje błąd, również w EN — nie ma polskiego fallbacku.- Klucze są pogrupowane w
nav.*,form.*,ui.*,page.*,component.*,lesson.*itd. Istniejące identyfikatorytext_XXsą stałe; dodanie akapitu nie powinno powodować ich renumeracji. Nowe klucze powinny opisywać przeznaczenie tekstu. - Linki, akcje formularzy i przekierowania buduj przez
PathService::url('/path'). Do zasobów używaj istniejących helperów zasobów, które nie dodają prefiksu języka. - Szablony korzystają z tych samych plików PHP w obu językach. Fragmenty treści są wykonywane przez
includez buforowaniem wyjścia. - Do JavaScript trafia wyłącznie sekcja
ui.*, w bezpiecznie zakodowanym JSON. Teksty w TypeScript pobieraj przeztranslate(...); nie umieszczaj komunikatów interfejsu w CSS. - Komunikaty walidacji zapisuj przez
FlashMessageService::translatedError/translatedInfo, aby język był ustalany przy wyświetlaniu. - Każda strona ma właściwe
lang,Content-Language, tytuł i opis. Strony GET mają alternatywne adresyhreflangPL/EN; błędy 404 mają lokalizowaną treść.
Marki Vulnerability Vault, BREACH//LAB, PromptLab, nazwy technologii i podatności
SQL Injection, Command Injection, XSS Reflected, XSS Stored pozostają bez tłumaczenia.
Stosujemy terminy laboratorium, podatny wariant, zabezpieczony wariant, dane wejściowe, wynik testu.
„Payload” wyjaśniamy przy pierwszym użyciu w lekcji. Nazwa ćwiczenia to Przesyłanie plików / File Upload,
ale adres pozostaje file-uploader. Polecenia, kod, payloady, nazwy pól backendowych, komentarze użytkowników
oraz surowe wyniki narzędzi pozostają niezmienione. Celowo podatne miejsca demonstracyjne nadal wyświetlają dane tak jak wcześniej;
escapowanie tłumaczeń nie jest zabezpieczeniem tych ćwiczeń.
python3 tests/public_package.py
php tests/public_comments.php
php tests/web_paths.php
php tests/security_hardening.php
unzip dist/vulnerability-vault-web.zip -d /tmp/vault-public-check
php tests/public_security.php /tmp/vault-public-checkpublic_package.py sprawdza allowlisty obu paczek i brak prywatnych plików w WWW.
public_security.php analizuje wykonywalne tokeny PHP paczki publicznej (pomijając
inertny kod edukacyjny) i sprawdza konfigurację trybów. public_comments.php
sprawdza granicę resetu UTC; z jawną konfiguracją dedykowanej bazy testowej oraz
VAULT_TEST_DB=1 sprawdza także limity, uprawnienia i równoległe operacje.
Nie uruchamiaj testu bazy na danych rzeczywistych — wymaga bazy zakończonej _test.
Przykład dla osobnej przygotowanej bazy testowej (konfiguracja przez środowisko
lub prywatny config.php):
VAULT_TEST_DB=1 php tests/public_comments.php
# Dla już przygotowanej testowej instalacji HTTPS; web_http zapisuje komentarz:
VAULT_TEST_DB=1 VAULT_BASE_URL=https://lab-test.example.com python3 tests/web_http.py
PLAYWRIGHT_MODULE=/path/to/node_modules/playwright VAULT_BASE_URL=https://lab-test.example.com \
node tests/web_demo.cjsCały zestaw integracyjny może sam utworzyć i usunąć osobną instancję MariaDB, bazy, paczki, katalog sesji i proxy HTTPS, korzystając wyłącznie z localhost:
PLAYWRIGHT_MODULE=/path/to/node_modules/playwright python3 tests/public_integration.pyWymaga PHP z PDO MySQL, MariaDB (mariadb-install-db, mariadbd), OpenSSL i Node.js.
Opcjonalne VAULT_PHP_BINARY, VAULT_MARIADB_BASEDIR, VAULT_NODE_BINARY oraz
PHPRC pozwalają wskazać runtime. Playwright jest opcjonalny; bez niego test
raportuje pominięcie przeglądarki. Test publiczny sprawdza również cookie Secure
za proxy bez nagłówka HTTPS oraz 503 przy rzeczywistej blokadzie InnoDB.
Lokalne ćwiczenia są sprawdzane na osobnej bazie i rozpakowanym pakiecie lokalnym.
Skrypt nie łączy się z istniejącą bazą. Certyfikat testowy istnieje tylko w katalogu
tymczasowym; samodzielny web_http.py może ufać mu przez VAULT_TEST_CA.
To nie zastępuje odbioru konfiguracji Apache i limitów operatora hostingu.
Test przeglądarki uruchamiaj po web_http.py, który zapisuje jego marker XSS.
Blokuje zewnętrzne żądania i sprawdza PL/EN, formularze bez wysyłania danych,
bezpieczne wyświetlanie nazw plików, wykonanie XSS po ostrzeżeniu i brak JavaScript.
Dodatkowy tests/local_http.py sprawdza pięć lokalnych ćwiczeń na dedykowanej bazie
przez serwer testowy; uruchom go z VAULT_TEST_DB=1 i VAULT_BASE_URL tej instalacji.
Pełny test stacku Apache/Docker pozostaje w tests/docker_smoke.py.
npm run check:i18n
npm run check
npm run build
python3 tests/docker_smoke.pyKontrola tłumaczeń sprawdza zestawy kluczy, niepuste wartości, zgodność parametrów i odwołania w PHP/TypeScript.
Test integracyjny sprawdza oba języki, aliasy, metadane, 404, przekierowania, SQL Injection, XSS Reflected/Stored,
Command Injection oraz uploady. Wykonuje też test serwisu tłumaczeń i zachowanego szablonu galerii z tests/i18n.php.
Używa osobnego projektu Compose vault-test-<identyfikator>; po teście usuwa jego kontenery, zachowując wolumeny.
Znana wcześniejsza rozbieżność: błędne logowanie w zabezpieczonym wariancie zwraca 302 zamiast zamierzonego 404,
ponieważ RedirectView ustawia nagłówek Location. Test raportuje ją osobno jako KNOWN ISSUE; lokalizacja nie zmienia tego mechanizmu.
Stare kontrolery galerii nie mają kompletnego zaplecza (User::getCurrentUser, dawny model obrazów).
Ich szablony i komunikaty są przetłumaczone, ale ta zmiana nie odbudowuje funkcjonalności galerii.
Przy zmianach treści sprawdź obie wersje z JavaScript i bez niego przy szerokościach 320, 390, 768 i 1440 px. Zwróć uwagę na hero, widoczność przełącznika bez otwierania menu, długie etykiety, stopkę i aktywne laboratorium na wyniku uploadu.
Automatyczny przegląd widoków w przeglądarce: tests/browser_i18n.cjs (wymaga Playwright z Chromium).
Jeśli Playwright jest zainstalowany poza projektem, podaj ścieżkę pakietu:
PLAYWRIGHT_MODULE=/path/to/node_modules/playwright node tests/browser_i18n.cjsDomyślnie test otwiera http://localhost:8080; inny adres można ustawić przez VAULT_BASE_URL.
Raport i zrzuty ekranu zapisuje w nowym katalogu tymczasowym, którego adres wypisuje na końcu.
Baza dokumentu to font-size: 100%, więc strona respektuje domyślny rozmiar tekstu ustawiony w przeglądarce.
Przy domyślnej bazie 16px treść ma 18px, opisy, formularze i stopka 16px, a drugorzędne etykiety oraz kod minimum 14px.
To przyjęta skala projektu, a nie uniwersalne minimum liczby pikseli określone przez WCAG.
Wielkości nagłówków są względne; tekst można powiększyć do 200%. Kolumny i etykiety zawijają się,
a przykłady kodu mają własne przewijanie poziome. Reguły skali i elastyczności układu są w
web/resources/scss/components/_typography.scss.
Test tests/typography.cjs sprawdza rzeczywiste wielkości widocznego tekstu i przepełnienie strony dla PL/EN,
320/390/768/1440px i rozmiaru tekstu 100%/200%. Kontroluje też spójność nagłówków sekcji,
ich przewagę nad tekstem opisów oraz brak nachodzenia terminala na podpis.
Wymaga tej samej instalacji Playwright co test języków:
PLAYWRIGHT_MODULE=/path/to/node_modules/playwright node tests/typography.cjs