Część 17 kursu PHP pod kątem INF.03: Bezpieczeństwo: SQL injection. Teoria w skrócie i 16 ćwiczeń z automatycznym sprawdzaniem.
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.
$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]);
$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]);
Na czym polega SQL injection?
Poprawna: b) Użytkownik dopisuje SQL do danych formularza.
Zapytanie przygotowane rozdziela kod SQL od danych. Najpierw wysyłamy szkielet z pytajnikami, potem osobno wartości — baza nigdy nie potraktuje ich jak polecenia.
$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);
$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);
Co robi prepare()?
Poprawna: a) Prepare tworzy zapytanie z parametrami.
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.
$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();
$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();
Czy prepared statements wystarczą?
Poprawna: d) Walidacja pilnuje sensu i formatu danych.