Panel hakera – SQL Injection vs bezpieczne logowanie

Warsztat INF.03 / DBI – technik informatyk

SQL Injection PHP + MySQL bezpieczne logowanie

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

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).

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.

Zadanie 3. Licznik nieudanych prób logowania

Dodaj prosty mechanizm liczenia nieudanych prób logowania w czasie sesji użytkownika.

Zadanie 4. Prosty log zdarzeń

Utwórz plik tekstowy log_logowanie.txt i zapisuj w nim najważniejsze zdarzenia.

Zadanie 5 (dla chętnych). Ekran powitalny po zalogowaniu

Po poprawnym logowaniu przekieruj użytkownika do strony „panel.php”.

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:

W zeszycie zapisz (nie podawaj prawdziwego hasła!):

Zadanie 2. Analiza prostego ataku SQL Injection (Moduł 2)

Skorzystaj z symulatora SQL Injection na tej stronie.

  1. Wpisz zwykły login (np. admin) – zanotuj, jak wygląda zbudowane zapytanie SQL.
  2. Wpisz payload ' OR 1=1 -- – zanotuj nowe zapytanie SQL.
  3. Podkreśl fragment, który „psuje” logikę logowania (np. OR 1=1).
  4. 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):

Zadanie 4. Twój wynik z checklisty bezpieczeństwa (Moduł 4)

Uzupełnij checklistę na stronie, a następnie:

Zadanie 5. Porównanie dwóch haseł pod kątem brute‑force (Moduł 5)

Wymyśl dwa przykładowe hasła:

Dla obu haseł:

Na koniec: jednym zdaniem wyjaśnij, dlaczego w kontekście DBI tak bardzo mówimy o „długości i unikalności haseł”.