W budowie
Prezentacja modułu

Część 17 · Bezpieczeństwo: SQL injection — kurs PHP INF.03

Część 17 kursu PHP pod kątem INF.03: Bezpieczeństwo: SQL injection. Teoria w skrócie i 16 ćwiczeń z automatycznym sprawdzaniem.

  • 45 min
  • 16 slajdów
  • Notatki nauczyciela: N
  • Strona modułu: otwórz
Plan · 45 min

Co dziś robimy

  1. 12 minJak działa SQL injection Klasyczny przykład: pole login z wartością ' OR 1=1 -- zamyka apostrof i sprawia, że…
  2. 12 minZapytania przygotowane W PDO używamy prepare() i execute([...]) .
  3. 12 minWalidacja i uprawnienia bazy Drugi filar to minimalne uprawnienia konta bazy.
  4. 4 minĆwiczenia 16 zadań w module — wybierz 3–4 na lekcję
  5. 5 minPodsumowanie bilet wyjściowy i zadanie domowe
Atom 1 z 4 · ~12 min

Jak działa SQL injection

SQL injection polega na dopisaniu fragmentu SQL do danych z formularza. Jeśli zapytanie powstaje przez sklejenie tekstu, użytkownik może zmienić jego sens.

  • Klasyczny przykład: pole login z wartością ' OR 1=1 -- zamyka apostrof i sprawia, że warunek staje się zawsze prawdziwy.
  • Wtedy logowanie przechodzi bez hasła.
  • Skutki to nie tylko obejście logowania: odczyt innych tabel, zmiana danych, a przy błędnej konfiguracji nawet usunięcie tabel.
  • Ryzyko rośnie, gdy konto bazy ma szerokie uprawnienia.
Kod · atom 1 · ~3 min

Jak to wygląda w kodzie

$login = $_POST['login'] ?? '';

// podatne: dane staja sie czescia zapytania
$sql = "SELECT id FROM users WHERE login = '$login'";

// bezpieczne: osobne zapytanie i osobne dane
$stmt = $pdo->prepare('SELECT id FROM users WHERE login = ?');
$stmt->execute([$login]);
Przykład na żywo · atom 1 · ~3 min

Uruchom i pokaż

$login = $_POST['login'] ?? '';

// podatne: dane staja sie czescia zapytania
$sql = "SELECT id FROM users WHERE login = '$login'";

// bezpieczne: osobne zapytanie i osobne dane
$stmt = $pdo->prepare('SELECT id FROM users WHERE login = ?');
$stmt->execute([$login]);
Sprawdzenie · atom 1 · ~2 min

Pytanie do klasy

Na czym polega SQL injection?

Atom 2 z 4 · ~12 min

Zapytania przygotowane

Zapytanie przygotowane rozdziela kod SQL od danych. Najpierw wysyłamy szkielet z pytajnikami, potem osobno wartości — baza nigdy nie potraktuje ich jak polecenia.

  • W PDO używamy prepare() i execute([...]) .
  • W mysqli jest mysqli_prepare() oraz wiązanie parametrów przez bind_param() .
  • To podstawowe zabezpieczenie i jednocześnie wygodny sposób pracy: to samo zapytanie można wykonać wiele razy z różnymi danymi, bez budowania tekstu od nowa.
Kod · atom 2 · ~3 min

Jak to wygląda w kodzie

$stmt = $pdo->prepare('SELECT id, imie FROM uczniowie WHERE klasa = ? AND punkty > ?');
$stmt->execute(['3TI', 10]);

foreach ($stmt as $w) {
    echo htmlspecialchars($w['imie'], ENT_QUOTES, 'UTF-8');
}

// mysqli
$stmt2 = mysqli_prepare($polaczenie, 'SELECT imie FROM uczniowie WHERE klasa = ?');
mysqli_stmt_bind_param($stmt2, 's', $klasa);
mysqli_stmt_execute($stmt2);
Przykład na żywo · atom 2 · ~3 min

Uruchom i pokaż

$stmt = $pdo->prepare('SELECT id, imie FROM uczniowie WHERE klasa = ? AND punkty > ?');
$stmt->execute(['3TI', 10]);

foreach ($stmt as $w) {
    echo htmlspecialchars($w['imie'], ENT_QUOTES, 'UTF-8');
}

// mysqli
$stmt2 = mysqli_prepare($polaczenie, 'SELECT imie FROM uczniowie WHERE klasa = ?');
mysqli_stmt_bind_param($stmt2, 's', $klasa);
mysqli_stmt_execute($stmt2);
Sprawdzenie · atom 2 · ~2 min

Pytanie do klasy

Co robi prepare()?

Atom 3 z 4 · ~12 min

Walidacja i uprawnienia bazy

Zapytania przygotowane chronią przed wstrzyknięciem, ale nie zastępują walidacji. Nadal sprawdzamy typ, zakres i długość danych, bo błędne wartości psują logikę aplikacji.

  • Drugi filar to minimalne uprawnienia konta bazy.
  • Konto aplikacji powinno mieć prawo odczytu i zapisu w swoich tabelach, a nie prawo usuwania bazy.
  • Warto też wyłączać szczegółowe komunikaty błędów na produkcji.
  • Komunikat z treścią zapytania ułatwia atakującemu poznanie struktury bazy.
Kod · atom 3 · ~3 min

Jak to wygląda w kodzie

$klasa = trim($_GET['klasa'] ?? '');
if (!preg_match('/^[1-4][A-Z]{2}$/', $klasa)) {
    exit('Nieprawidlowa klasa');
}

$stmt = $pdo->prepare('SELECT imie FROM uczniowie WHERE klasa = ?');
$stmt->execute([$klasa]);
$dane = $stmt->fetchAll();
Przykład na żywo · atom 3 · ~3 min

Uruchom i pokaż

$klasa = trim($_GET['klasa'] ?? '');
if (!preg_match('/^[1-4][A-Z]{2}$/', $klasa)) {
    exit('Nieprawidlowa klasa');
}

$stmt = $pdo->prepare('SELECT imie FROM uczniowie WHERE klasa = ?');
$stmt->execute([$klasa]);
$dane = $stmt->fetchAll();
Sprawdzenie · atom 3 · ~2 min

Pytanie do klasy

Czy prepared statements wystarczą?

Zamknięcie · ~3 min

Co zabrać z lekcji

  • Klasyczny przykład: pole login z wartością ' OR 1=1 -- zamyka apostrof i sprawia, że warunek staje się zawsze prawdziwy.
  • Zapytanie przygotowane rozdziela kod SQL od danych.
  • Drugi filar to minimalne uprawnienia konta bazy.
  • Ćwiczenia
Bilet wyjściowy: Dlaczego ukrywamy błędy SQL?
Odpowiedź: a) Zdradzają strukturę
Hyżyk· Część 17 · Bezpieczeństwo: SQL injection — kurs PHP INF.03 moduł 1 / 1