Načrtovanje in razvoj spletnih aplikacij

Vstavljanje podatkov v podatkovno zbirko - INSERT

Podatke v tabelo pogosto dodajamo prek spletnega obrazca. Uporabnik vnese podatke, PHP jih prebere, preveri in nato shrani v podatkovno zbirko z ukazom INSERT INTO.

Pri vnosu podatkov je pomembno, da ločimo obrazec, obdelavo poslanih podatkov in zapis v podatkovno bazo. Obrazec je namenjen vnosu, obdelovalna skripta pa preverjanju, varovanju in shranjevanju podatkov.

Pomni: Podatkov, ki pridejo iz obrazca, ne zapišemo neposredno v poizvedbo. Pred shranjevanjem jih preverimo in za vnos v podatkovno bazo uporabimo pripravljene stavke.

Pripravljeni stavki ločijo ukaz od podatkov. To pomeni, da se vrednosti iz obrazca ne lepijo neposredno v besedilo poizvedbe, ampak se vežejo na vnaprej pripravljena mesta.

Vsebina strani

Osnovna pravila

Pri vnosu podatkov v tabelo prek obrazca moramo poskrbeti za pravilno zgradbo obrazca, preverjanje poslanih podatkov in varen zapis v podatkovno bazo.

  • Obrazec naj vsebuje polja za vse podatke, ki jih želimo shraniti.
  • Podatke iz obrazca beremo iz polja $_POST, kadar obrazec uporablja metodo POST.
  • Besedilne podatke običajno obrežemo s funkcijo trim().
  • Številske podatke pretvorimo v ustrezen tip, na primer z (int) ali (float).
  • Pred zapisom preverimo, ali so obvezna polja izpolnjena.
  • Za vnos v podatkovno bazo uporabimo pripravljen stavek.
  • Obrazec zaščitimo pred neželenimi zahtevki, na primer s CSRF žetonom.

Pozor: Preverjanje podatkov v brskalniku ni dovolj. Podatke je treba vedno preveriti tudi na strežniku, ker lahko uporabnik ali napadalec pošlje zahtevek mimo običajnega obrazca.

Vnos podatkov v tabelo

Pri vnosu podatkov v spletni aplikaciji običajno pripravimo obrazec, v katerega uporabnik vnese podatke. Ob oddaji obrazca strežniška koda preveri pravilnost podatkov in jih nato shrani v tabelo.

Osnovna sintaksa za vnos enega zapisa je:

INSERT INTO imeTabele (stolpec1, stolpec2, stolpec3)
VALUES (vrednost1, vrednost2, vrednost3);

Pri neposrednem zapisu v učnih primerih so vrednosti vidne v poizvedbi. V spletni aplikaciji pa namesto neposrednih vrednosti uporabimo pripravljena mesta oziroma parametre.

INSERT INTO knjige
(Priimek_avtorja, Ime_avtorja, Naslov, Strani, Cena, Leto)
VALUES
(:priimek, :ime, :naslov, :strani, :cena, :leto);

Tak zapis je preglednejši in varnejši, saj podatki iz obrazca niso neposredno vključeni v besedilo poizvedbe.

Pomni: Ukaz INSERT INTO doda nov zapis v tabelo. Za spreminjanje obstoječih zapisov uporabimo UPDATE, za brisanje pa DELETE.

Osnovni primer z mysqli

Spodnji zgled prikazuje preprost vnos podatkov iz obrazca v tabelo knjige z uporabo pripravljenega stavka.

<?php
define('DB_SERVER', 'localhost');
define('DB_USER', 'uporabnik');
define('DB_PASS', 'skritoGeslo');
define('DB_NAME', 'knjiznica');

// Povezava do podatkovne zbirke
$connection = mysqli_connect(DB_SERVER, DB_USER, DB_PASS, DB_NAME);

// Preverjanje povezave
if (!$connection) {
    die(
        'Povezava s podatkovno zbirko ni vzpostavljena: ' .
        mysqli_connect_error() .
        ' (' . mysqli_connect_errno() . ')'
    );
}

if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    $priimek = trim($_POST['priimek'] ?? '');
    $ime = trim($_POST['ime'] ?? '');
    $naslov = trim($_POST['naslov'] ?? '');
    $strani = (int)($_POST['strani'] ?? 0);
    $cena = (float)($_POST['cena'] ?? 0);
    $leto = (int)($_POST['leto'] ?? 0);

    $stmt = mysqli_prepare(
        $connection,
        "INSERT INTO knjige
        (Priimek_avtorja, Ime_avtorja, Naslov, Strani, Cena, Leto)
        VALUES (?, ?, ?, ?, ?, ?)"
    );

    mysqli_stmt_bind_param($stmt, 'sssidi', $priimek, $ime, $naslov, $strani, $cena, $leto);

    if (mysqli_stmt_execute($stmt)) {
        echo 'Zapis je bil uspešno dodan.';
    } else {
        echo 'Napaka pri vnosu podatkov.';
    }

    mysqli_stmt_close($stmt);
}

mysqli_close($connection);
?>

Pri mysqli pripravljen stavek pripravimo s funkcijo mysqli_prepare(), vrednosti povežemo s funkcijo mysqli_stmt_bind_param(), stavek pa izvedemo s funkcijo mysqli_stmt_execute().

Osnovni primer s PDO

Tudi z vmesnikom PDO podatke praviloma vnašamo s pripravljenimi stavki in vezanimi parametri.

<?php
$streznik = 'localhost';
$baza = 'knjiznica';
$uporabnik = 'uporabnik';
$geslo = 'skritoGeslo';

try {
    // Povezava do podatkovne zbirke
    $pdo = new PDO("mysql:host=$streznik;dbname=$baza;charset=utf8mb4", $uporabnik, $geslo);

    // Vklop izjem pri napakah
    $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

    if ($_SERVER['REQUEST_METHOD'] === 'POST') {
        $priimek = trim($_POST['priimek'] ?? '');
        $ime = trim($_POST['ime'] ?? '');
        $naslov = trim($_POST['naslov'] ?? '');
        $strani = (int)($_POST['strani'] ?? 0);
        $cena = (float)($_POST['cena'] ?? 0);
        $leto = (int)($_POST['leto'] ?? 0);

        $stmt = $pdo->prepare(
            "INSERT INTO knjige
            (Priimek_avtorja, Ime_avtorja, Naslov, Strani, Cena, Leto)
            VALUES
            (:priimek, :ime, :naslov, :strani, :cena, :leto)"
        );

        $stmt->bindValue(':priimek', $priimek, PDO::PARAM_STR);
        $stmt->bindValue(':ime', $ime, PDO::PARAM_STR);
        $stmt->bindValue(':naslov', $naslov, PDO::PARAM_STR);
        $stmt->bindValue(':strani', $strani, PDO::PARAM_INT);
        $stmt->bindValue(':cena', number_format($cena, 2, '.', ''), PDO::PARAM_STR);
        $stmt->bindValue(':leto', $leto, PDO::PARAM_INT);

        $stmt->execute();

        echo 'Zapis je bil uspešno dodan.';
    }
}
catch (PDOException $e) {
    echo 'Napaka pri vnosu podatkov: ' . $e->getMessage();
}
?>

Pri PDO pripravljen stavek pripravimo z metodo prepare(), vrednosti povežemo z metodo bindValue(), stavek pa izvedemo z metodo execute().

Zaščita obrazca s CSRF žetonom

Pri obrazcih ni pomembno le preverjanje podatkov, ampak tudi zaščita pred zlorabami. Ena od pogostih zaščit je uporaba CSRF žetona.

CSRF je kratica za Cross-Site Request Forgery. Gre za napad, pri katerem bi napadalec skušal doseči, da uporabnikov brskalnik pošlje zahtevek naši aplikaciji brez njegove vednosti.

To preprečimo tako, da ob prikazu obrazca ustvarimo poseben žeton, ga shranimo v sejo in dodamo v obrazec kot skrito polje. Ko uporabnik obrazec odda, preverimo, ali se poslani žeton ujema z vrednostjo v seji.

<?php
// Ustvarjanje CSRF žetona
$_SESSION['csrf_zeton'] = bin2hex(random_bytes(32));
?>

<input type="hidden" name="csrf_zeton" value="<?php echo $_SESSION['csrf_zeton']; ?>">

Pri obdelavi obrazca nato preverimo veljavnost žetona. Če žeton manjka ali ni pravilen, obrazca ne obdelamo.

<?php
if (
    !isset($_POST['csrf_zeton'], $_SESSION['csrf_zeton']) ||
    $_POST['csrf_zeton'] !== $_SESSION['csrf_zeton']
) {
    die('Neveljaven obrazec.');
}
?>

Pozor: CSRF žeton ne nadomesti preverjanja vnesenih podatkov. Žeton preverja izvor oziroma veljavnost oddaje obrazca, podatke pa je treba kljub temu posebej preveriti.

Preverjanje in priprava podatkov

Pred zapisom v podatkovno bazo je treba preveriti, ali so podatki smiselni. Besedilna polja ne smejo biti prazna, številske vrednosti pa morajo biti v pričakovanem obsegu.

Podatek Preverjanje Primer priprave
Priimek_avtorja Podatek naj ne bo prazen. trim($_POST['priimek'] ?? '')
Ime_avtorja Podatek naj ne bo prazen. trim($_POST['ime'] ?? '')
Naslov Podatek naj ne bo prazen. trim($_POST['naslov'] ?? '')
Strani Vrednost naj bo pozitivno celo število. (int)($_POST['strani'] ?? 0)
Cena Vrednost naj bo pozitivno decimalno število. (float)($_POST['cena'] ?? 0)
Leto Vrednost naj bo smiselno leto. (int)($_POST['leto'] ?? 0)

Pomni: Čiščenje, pretvorba in preverjanje podatkov niso isto. trim() odstrani odvečne presledke, pretvorba spremeni tip podatka, preverjanje pa ugotovi, ali je vrednost sprejemljiva.

Primerjava pristopov mysqli in PDO

Oba pristopa omogočata varen vnos podatkov s pripravljenimi stavki, razlikujeta pa se v zapisu in načinu vezave parametrov.

Korak mysqli PDO
Priprava stavka mysqli_prepare() $pdo->prepare()
Vezava vrednosti mysqli_stmt_bind_param() $stmt->bindValue()
Oznake parametrov ? :priimek, :ime, :naslov
Izvedba mysqli_stmt_execute() $stmt->execute()
Obravnava napak Preverjanje rezultata izvedbe. Pogosto z izjemami try in catch.

Aplikacija Knjige

📘Aplikacija Knjige

V priloženi aplikaciji Knjige sta vnos in obdelava razdeljena na dve datoteki: 06_vnos-obrazec.php pripravi obrazec za vnos podatkov o knjigi, 06_vnos.php pa izvede preverjanje, validacijo in shranjevanje v tabelo knjige.

Obrazec vsebuje polja za priimek avtorja, ime avtorja, naslov knjige, število strani, ceno in leto izida, poleg tega pa uporablja tudi CSRF žeton za zaščito obrazca.

Obdelava vnosa preveri poslane podatke, veljavnost CSRF žetona, izpiše morebitne napake, nato pa zapis shrani z varnim pripravljenim stavkom PDO. Po uspešnem vnosu se izpiše tudi posodobljen seznam vseh knjig.

Primer: aplikacija Knjige – 06_vnos-obrazec.php
Primer: aplikacija Knjige – 06_vnos.php
  1. Najprej pripravimo obrazec za vnos podatkov.
  2. V obrazec dodamo polja za vse podatke, ki jih želimo shraniti.
  3. Ob prikazu obrazca ustvarimo in shranimo CSRF žeton.
  4. Ob oddaji obrazca podatke preberemo v PHP.
  5. Preverimo veljavnost CSRF žetona.
  6. Podatke očistimo, pretvorimo in preverimo njihovo pravilnost.
  7. Za vnos v bazo uporabimo pripravljen stavek INSERT INTO.
  8. Po uspešnem vnosu prikažemo potrditveno sporočilo in po potrebi še posodobljen seznam zapisov.

Pozor: Obdelovalna datoteka, na primer 06_vnos.php, mora preveriti, ali je bil obrazec res poslan z metodo POST. Tako se izognemo nepredvideni obdelavi praznih ali neustreznih zahtevkov.

Priporočila

  • Obrazec in obdelavo vnosa razdeli v jasno ločene korake.
  • Za vnos podatkov v bazo uporabi pripravljen stavek.
  • Ne zanašaj se samo na preverjanje v brskalniku.
  • Besedilne podatke obreži s funkcijo trim().
  • Številske podatke pretvori in preveri, ali so v dovoljenem obsegu.
  • Obrazec zaščiti s CSRF žetonom.
  • Po uspešnem vnosu prikaži jasno potrditveno sporočilo.
  • Pri izpisu vnesenih podatkov uporabi htmlspecialchars().

Pogoste napake

  • Neposredno sestavljanje poizvedbe iz podatkov obrazca.
  • Manjkajoče preverjanje, ali je zahtevek poslan z metodo POST.
  • Manjkajoče preverjanje obveznih polj.
  • Številski podatki niso pretvorjeni ali preverjeni.
  • Obrazec nima CSRF zaščite.
  • Število parametrov v poizvedbi se ne ujema s številom vezanih vrednosti.
  • Napake pri vnosu se uporabniku ne prikažejo razumljivo.
  • Po uspešnem vnosu se isti obrazec lahko ob osvežitvi strani ponovno pošlje.

Vnos podatkov v podatkovno bazo je občutljiv del aplikacije. Vsak podatek iz obrazca je treba obravnavati kot nezaupan, dokler ga strežniška koda ne preveri.