Walidacja i sanityzacja — bezpieczne dane wejściowe

🎯 Po tej lekcji będziesz walidować e-mail i liczby przez filter_var, czyścić dane przez trim i rozumieć różnicę między walidacją a sanityzacją.

W poprzedniej lekcji sprawdzałeś tylko, czy pole jest puste. Ale co jeśli ktoś wpisze „abc" zamiast adresu e-mail albo „sto lat" zamiast wieku? Pora nauczyć się walidacji i sanityzacji — dwóch nierozłącznych technik obsługi danych wejściowych.

Walidacja vs sanityzacja — różnica

Te dwa terminy brzmią podobnie, ale robią różne rzeczy:

  • Walidacjasprawdza, czy dane spełniają wymagania. Odpowiada na pytanie: „czy ten email jest poprawny?". Wynik: tak lub nie (dane zostają niezmienione).
  • Sanityzacjaczyści lub przetwarza dane. Usuwa zbędne spacje, zamienia format, usuwa niedozwolone znaki. Dane mogą się zmienić.

W praktyce często robisz obie naraz: sanityzujesz (np. przez trim), a potem walidatujesz (np. przez filter_var).

trim — pierwszy krok sanityzacji

Zanim cokolwiek sprawdzisz, wyczyść dane z przypadkowych spacji na początku i końcu. Użytkownicy nagminnie wklejają tekst ze spacjami:

<?php
declare(strict_types=1);

$email = '  [email protected]   '; // użytkownik skopiował z maila
$email = trim($email);
echo $email; // wynik: [email protected]

Zasada: trim zawsze przed walidacją. Pole z samymi spacjami po trim stanie się pustym łańcuchem '' — łatwo sprawdzić wymagane pola.

filter_var — walidacja formatów

PHP dostarcza wbudowaną funkcję filter_var, która sprawdza wiele popularnych formatów danych. Przy flagach FILTER_VALIDATE_* zwraca przefiltrowaną wartość jeśli dane są poprawne, lub false jeśli nie.

Walidacja adresu e-mail

<?php
declare(strict_types=1);

$email = '[email protected]';
if (filter_var($email, FILTER_VALIDATE_EMAIL) !== false) {
    echo 'Adres e-mail jest poprawny.';
} else {
    echo 'Nieprawidłowy adres e-mail.';
}

// Kilka przykładów:
var_dump(filter_var('[email protected]',  FILTER_VALIDATE_EMAIL)); // string(15) "[email protected]"
var_dump(filter_var('nie-to-email',     FILTER_VALIDATE_EMAIL)); // wynik: bool(false)
var_dump(filter_var('@brak-nazwy.pl',   FILTER_VALIDATE_EMAIL)); // wynik: bool(false)
⚠️ Porównanie !== false: Użyj !== false, nie samego !. Dla FILTER_VALIDATE_INT wartość 0 jest prawidłowym wynikiem i jest falsy — !0 zwróciłoby true, czyli błędnie uznałoby zero za nieprawidłowe.

Walidacja liczby całkowitej

<?php
declare(strict_types=1);

$wiek = filter_var('25', FILTER_VALIDATE_INT);
var_dump($wiek); // wynik: int(25)

$blednyWiek = filter_var('dwadziescia', FILTER_VALIDATE_INT);
var_dump($blednyWiek); // wynik: bool(false)

// Walidacja z zakresem:
$opcje = ['options' => ['min_range' => 0, 'max_range' => 120]];
var_dump(filter_var(25,  FILTER_VALIDATE_INT, $opcje)); // wynik: int(25)
var_dump(filter_var(200, FILTER_VALIDATE_INT, $opcje)); // wynik: bool(false)
var_dump(filter_var(-1,  FILTER_VALIDATE_INT, $opcje)); // wynik: bool(false)

Kompletny przykład — formularz z walidacją

Oto formularz rejestracyjny łączący sanityzację, walidację i bezpieczne wyświetlanie danych:

<?php
declare(strict_types=1);

$imie  = '';
$email = '';
$wiek  = '';
$bledy = [];
$sukces = false;

if (isset($_POST['wyslij'])) {
    // 1. Sanityzacja — wyczyść dane z formularza
    $imie  = trim($_POST['imie']  ?? '');
    $email = trim($_POST['email'] ?? '');
    $wiek  = trim($_POST['wiek']  ?? '');

    // 2. Walidacja — sprawdź wymagania
    if ($imie === '') {
        $bledy[] = 'Imię jest wymagane.';
    } elseif (strlen($imie) < 2) {
        $bledy[] = 'Imię musi mieć co najmniej 2 znaki.';
    }

    if ($email === '') {
        $bledy[] = 'Adres e-mail jest wymagany.';
    } elseif (filter_var($email, FILTER_VALIDATE_EMAIL) === false) {
        $bledy[] = 'Podaj prawidłowy adres e-mail.';
    }

    if ($wiek === '') {
        $bledy[] = 'Wiek jest wymagany.';
    } else {
        $opcje = ['options' => ['min_range' => 0, 'max_range' => 120]];
        if (filter_var($wiek, FILTER_VALIDATE_INT, $opcje) === false) {
            $bledy[] = 'Wiek musi być liczbą całkowitą od 0 do 120.';
        }
    }

    if ($bledy === []) {
        // Wszystko poprawne — tutaj zapisałbyś dane do bazy
        $sukces = true;
    }
}
?>
<!DOCTYPE html>
<html lang="pl">
<head><meta charset="UTF-8"><title>Rejestracja</title></head>
<body>

<?php if ($sukces): ?>
    <p>Zarejestrowano: <?= htmlspecialchars($imie, ENT_QUOTES, 'UTF-8') ?>,
       <?= htmlspecialchars($email, ENT_QUOTES, 'UTF-8') ?>,
       wiek: <?= htmlspecialchars($wiek, ENT_QUOTES, 'UTF-8') ?></p>
<?php else: ?>
    <?php if ($bledy !== []): ?>
        <ul style="color:red">
        <?php foreach ($bledy as $blad): ?>
            <li><?= htmlspecialchars($blad, ENT_QUOTES, 'UTF-8') ?></li>
        <?php endforeach; ?>
        </ul>
    <?php endif; ?>
    <form method="post" action="">
        <label>Imię:
            <input type="text" name="imie"
                   value="<?= htmlspecialchars($imie, ENT_QUOTES, 'UTF-8') ?>">
        </label><br>
        <label>E-mail:
            <input type="email" name="email"
                   value="<?= htmlspecialchars($email, ENT_QUOTES, 'UTF-8') ?>">
        </label><br>
        <label>Wiek:
            <input type="number" name="wiek"
                   value="<?= htmlspecialchars($wiek, ENT_QUOTES, 'UTF-8') ?>">
        </label><br>
        <button type="submit" name="wyslij">Zarejestruj się</button>
    </form>
<?php endif; ?>

</body>
</html>

Podsumowanie nawyków bezpieczeństwa

| Krok | Co robisz | Funkcja | |---|---|---| | 1. Wejście | Wyczyść spacje | trim() | | 2. Wejście | Sprawdź format | filter_var() | | 3. Wyjście | Zabezpiecz HTML | htmlspecialchars() |

💡 Walidacja po stronie serwera jest obowiązkowa. Walidacja HTML5 (atrybut required, type="email") i JavaScript to wygoda dla użytkownika — nie bezpieczeństwo. Ktoś może wyłączyć JS lub wysłać żądanie HTTP ręcznie. Zawsze waliduj w PHP.

Masz już solidne fundamenty bezpieczeństwa formularzy. W kolejnej lekcji zajmiemy się sesjami i ciasteczkami — czyli tym, jak „zapamiętać" użytkownika między kolejnymi żądaniami.

Funkcje w tej lekcji

Sprawdź się

Co zwraca filter_var('sto', FILTER_VALIDATE_INT)?

Jaka jest kluczowa różnica między walidacją a sanityzacją?

Dlaczego trim() stosuje się PRZED sprawdzeniem czy pole jest puste?

Ćwiczenie

Napisz funkcję validateKontakt(string $imie, string $email, string $wiek): array, która zwraca tablicę błędów. Reguły: imię minimum 2 znaki, email poprawny format, wiek liczba całkowita 0–120. Funkcja powinna zwrócić pustą tablicę gdy wszystko jest OK.

Pokaż rozwiązanie
<?php
declare(strict_types=1);

function validateKontakt(string $imie, string $email, string $wiek): array
{
    $bledy = [];

    if (strlen($imie) < 2) {
        $bledy[] = 'Imię musi mieć co najmniej 2 znaki.';
    }

    if (filter_var($email, FILTER_VALIDATE_EMAIL) === false) {
        $bledy[] = 'Podaj prawidłowy adres e-mail.';
    }

    $opcje = ['options' => ['min_range' => 0, 'max_range' => 120]];
    if (filter_var($wiek, FILTER_VALIDATE_INT, $opcje) === false) {
        $bledy[] = 'Wiek musi być liczbą całkowitą od 0 do 120.';
    }

    return $bledy;
}

// Testy
print_r(validateKontakt('Ola', '[email protected]', '25'));  // []
print_r(validateKontakt('X', 'nie-email', '999'));          // 3 błędy