1. Założenia warsztatu
Celem tego panelu jest pokazanie, jak wygląda podatny formularz logowania w PHP oraz jak ten sam formularz można poprawić, używając zapytań parametryzowanych (prepared statements).
Uczniowie najpierw „atakują” wersję podatną na SQL Injection, a potem dostają gotowy schemat poprawnego logowania do wykorzystania w swoich projektach.
2. Demo formularza logowania (symulacja)
Instrukcja: wpisz dowolny login i hasło. Następnie zamiast normalnego loginu
spróbuj użyć ciągu ' OR 1=1 --. Zobaczysz, jak zmienia się
generowane zapytanie SQL i wynik „logowania”. To tylko symulacja – żadna prawdziwa baza nie jest podłączona.
Dane logowania (symulacja)
Jak wygląda podatne zapytanie?
Skrypt buduje zapytanie łącząc tekst SQL z danymi z formularza:
SELECT * FROM users
WHERE login = '<login_z_formularza>'
AND password = '<haslo_z_formularza>';
Jeśli w miejscu loginu pojawi się konstrukcja typu ' OR 1=1 --,
warunek OR 1=1 sprawi, że zapytanie „przepuści” wszystkich.
3. Kod „przed” – podatny formularz PHP (wersja tylko do analizy)
To uproszczony przykład pokazujący typowy błąd: wklejanie danych z formularza bezpośrednio w treść zapytania SQL. Kod jest tylko do omówienia, nie powinien być używany w prawdziwych projektach.
<?php
// Połączenie z bazą (przykład – dane dostępu do uzupełnienia)
$conn = mysqli_connect("localhost", "root", "", "moja_baza");
if (!$conn) {
die("Błąd połączenia: " . mysqli_connect_error());
}
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$login = $_POST['login'] ?? '';
$haslo = $_POST['haslo'] ?? '';
// ❌ BŁĄD: wklejanie danych z formularza wprost do zapytania
$sql = "SELECT * FROM users
WHERE login = '$login'
AND password = '$haslo'";
echo "<p>Zapytanie SQL:</p>";
echo "<pre>$sql</pre>";
$result = mysqli_query($conn, $sql);
if ($result && mysqli_num_rows($result) > 0) {
echo "<p>Zalogowano (podatny kod)!</p>";
} else {
echo "<p>Błędny login lub hasło.</p>";
}
}
mysqli_close($conn);
?>
Już samo wyświetlanie pełnego zapytania SQL z wstrzykniętym payloadem pokazuje uczniom, jak łatwo manipulować kodem, jeśli nie ma walidacji i zapytań parametryzowanych.
4. Kod „po” – bezpieczniejsze logowanie z zapytaniem przygotowanym
W poprawionej wersji używamy zapytania przygotowanego (prepared statement). Dane z formularza są wiązane jako parametry, dzięki czemu nie wstrzykujemy ich bezpośrednio do treści SQL.
<?php
$conn = mysqli_connect("localhost", "root", "", "moja_baza");
if (!$conn) {
die("Błąd połączenia: " . mysqli_connect_error());
}
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$login = $_POST['login'] ?? '';
$haslo = $_POST['haslo'] ?? '';
// ✅ Przygotowane zapytanie – dane trafiają jako parametry
$sql = "SELECT id, login, haslo_hash
FROM users
WHERE login = ?";
$stmt = mysqli_prepare($conn, $sql);
if ($stmt) {
mysqli_stmt_bind_param($stmt, "s", $login);
mysqli_stmt_execute($stmt);
$result = mysqli_stmt_get_result($stmt);
if ($result && mysqli_num_rows($result) === 1) {
$row = mysqli_fetch_assoc($result);
// haslo_hash – kolumna z zaszyfrowanym hasłem (password_hash)
if (password_verify($haslo, $row['haslo_hash'])) {
echo "<p>Zalogowano (bezpieczniejszy kod).</p>";
// tutaj można ustawić $_SESSION itd.
} else {
echo "<p>Błędne hasło.</p>";
}
} else {
echo "<p>Nie znaleziono użytkownika o takim loginie.</p>";
}
mysqli_stmt_close($stmt);
} else {
echo "<p>Błąd przygotowania zapytania.</p>";
}
}
mysqli_close($conn);
?>
Ten schemat można bezpośrednio przenieść do projektu INF.03:
zmieniasz nazwę tabeli, pola i dostosowujesz strukturę, ale idea jest ta sama –
nie łączysz stringów, tylko używasz parametrów i password_hash/password_verify.
5. Alternatywa – logowanie z użyciem PDO (dla chętnych)
Jeżeli w projekcie korzystasz z PDO, możesz użyć zapytań przygotowanych w bardzo podobny sposób. Poniższy kod to punkt wyjścia – nie trzeba go omawiać z całą klasą, ale chętni uczniowie mogą go wykorzystać w swoich projektach.
<?php
try {
$pdo = new PDO(
"mysql:host=localhost;dbname=moja_baza;charset=utf8mb4",
"root",
"",
[ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION ]
);
} catch (PDOException $e) {
die("Błąd połączenia: " . $e->getMessage());
}
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$login = $_POST['login'] ?? '';
$haslo = $_POST['haslo'] ?? '';
$stmt = $pdo->prepare("
SELECT id, login, haslo_hash
FROM users
WHERE login = :login
LIMIT 1
");
$stmt->bindParam(':login', $login, PDO::PARAM_STR);
$stmt->execute();
if ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
if (password_verify($haslo, $row['haslo_hash'])) {
echo "Zalogowano (PDO).";
} else {
echo "Błędne hasło.";
}
} else {
echo "Nie znaleziono użytkownika.";
}
}
?>
6. Jak poprowadzić lekcję z tym panelem
- Pokazujesz demo formularza (sekcja 2) i prosisz uczniów o spróbowanie payloadu
' OR 1=1 --. - Wspólnie analizujecie kod „przed” i szukacie miejsca, gdzie dane z formularza wchodzą w SQL.
- Potem omawiacie kod „po” z prepared statements i hashowaniem haseł, pokazując różnice.
- Na koniec zadanie: uczniowie mają przepisać swój prosty formularz logowania na wersję z zapytaniem przygotowanym.
7. Zadania dla ucznia – zastosuj panel w swoim projekcie
Zadanie 1. Własna tabela logowania
Zaadaptuj kod „bezpieczniejszy (mysqli)” do swojej bazy danych.
Zastąp tabelę users własną tabelą (np. uczniowie_logowanie).
- Utwórz w bazie tabelę z kolumnami:
id,login,haslo_hash. - Dodaj przynajmniej dwóch użytkowników, zapisując hasło funkcją
password_hash()w osobnym skrypcie „rejestracja-test.php”. - Dostosuj zapytanie w kodzie tak, aby odwoływało się do Twojej tabeli i kolumn.
- Przetestuj logowanie dla poprawnych i błędnych danych.
Zadanie 2. Walidacja pustych pól
Rozbuduj formularz logowania tak, aby przed wykonaniem zapytania do bazy sprawdzał, czy pola login i hasło nie są puste.
- Jeśli pole jest puste, wyświetl czytelny komunikat (bez wykonywania zapytania SQL).
- Dodaj podstawowe filtrowanie danych, np.
trim()i ograniczenie długości (strlen). - Zadbaj, aby komunikaty błędów nie zdradzały, czy dany login istnieje w bazie (wystarczy ogólne „Błędne dane logowania”).
Zadanie 3. Licznik nieudanych prób logowania
Dodaj prosty mechanizm liczenia nieudanych prób logowania w czasie sesji użytkownika.
- Po nieudanym logowaniu zwiększ wartość licznika w
$_SESSION. - Po przekroczeniu np. 5 prób wyświetl komunikat „Zbyt wiele prób – spróbuj za kilka minut”.
- Wyświetl aktualną liczbę prób nieudanego logowania pod formularzem (np. „Próba 3/5”).
Zadanie 4. Prosty log zdarzeń
Utwórz plik tekstowy log_logowanie.txt i zapisuj w nim najważniejsze zdarzenia.
- Przy każdej próbie logowania dopisz wiersz z datą, adresem IP i statusem (sukces / porażka).
- Upewnij się, że plik logu nie jest dostępny bezpośrednio z poziomu przeglądarki (np. przez odpowiednią lokalizację pliku / konfigurację serwera).
- Omów na lekcji, dlaczego logowanie zdarzeń pomaga w wykrywaniu prób ataków SQL Injection.
Zadanie 5 (dla chętnych). Ekran powitalny po zalogowaniu
Po poprawnym logowaniu przekieruj użytkownika do strony „panel.php”.
- Przekaż w sesji informację o zalogowanym użytkowniku (np.
$_SESSION['login']). - Na „panel.php” wyświetl powitanie oraz informację o ostatnim logowaniu (prosta kolumna w bazie lub odczyt z logu).
- Dodaj przycisk „Wyloguj”, który niszczy sesję i wraca do formularza logowania.
8. Zadania DBI 2026 – CyberLab (dla klasy 4TI)
Poniższe zadania odnoszą się bezpośrednio do modułów na tej stronie. Uczniowie mogą rozwiązywać je samodzielnie, w parach lub w małych zespołach.
Zadanie 1. Zaprojektuj „idealne” hasło (Moduł 1 + 5)
Korzystając z testera siły hasła i Brute‑Force Lab, zaprojektuj hasło, które jest jednocześnie:
- łatwe do zapamiętania dla Ciebie (np. zdanie, akronim, fraza),
- trudne do złamania dla atakującego (dużo kombinacji, długi czas brute‑force).
W zeszycie zapisz (nie podawaj prawdziwego hasła!):
- strategię tworzenia hasła (np. „pierwsze litery z ulubionego cytatu + rok + znak specjalny”),
- jaką długość i jaki zestaw znaków uważasz za minimum dla konta e‑mail / bankowości / social media.
Zadanie 2. Analiza prostego ataku SQL Injection (Moduł 2)
Skorzystaj z symulatora SQL Injection na tej stronie.
- Wpisz zwykły login (np.
admin) – zanotuj, jak wygląda zbudowane zapytanie SQL. - Wpisz payload
' OR 1=1 --– zanotuj nowe zapytanie SQL. - Podkreśl fragment, który „psuje” logikę logowania (np.
OR 1=1). - Napisz jednym zdaniem, dlaczego to jest niebezpieczne i jak można się przed tym bronić (zapytania parametryzowane).
Zadanie 3. Raport z wycieku danych (Moduł 3)
Po uruchomieniu symulacji wycieku danych odpowiedz na pytania (w punktach, maksymalnie 1–2 zdania na punkt):
- Co jest największym problemem w zaprezentowanym wycieku (podaj konkretny przykład z ekranu – np. „admin:1234”).
- Jak należałoby przechowywać hasła, żeby taki wyciek był mniej groźny (podaj nazwę mechanizmu, np. „hashowanie, bcrypt”).
- Jakie mogą być skutki wycieku dla: a) użytkownika, b) szkoły / firmy (2–3 przykłady).
Zadanie 4. Twój wynik z checklisty bezpieczeństwa (Moduł 4)
Uzupełnij checklistę na stronie, a następnie:
- zanotuj swój wynik (np. „Poziom bezpieczeństwa: 2/4”),
- zapisz, które dwa nawyki chciałbyś/chciałabyś wprowadzić w ciągu najbliższego miesiąca (konkretnie, np. „włączę 2FA na koncie pocztowym”).
Zadanie 5. Porównanie dwóch haseł pod kątem brute‑force (Moduł 5)
Wymyśl dwa przykładowe hasła:
- Hasło A – krótkie i proste (np. 6 małych liter),
- Hasło B – dłuższe i złożone (np. 12 znaków, różne typy znaków).
Dla obu haseł:
- przyjmij odpowiednią długość i typ znaków w Brute‑Force Lab,
- zapisz liczbę kombinacji oraz przybliżony czas brute‑force, który wyświetli skrypt.
Na koniec: jednym zdaniem wyjaśnij, dlaczego w kontekście DBI tak bardzo mówimy o „długości i unikalności haseł”.