19. PHP – skrypty zmieniające dane w bazie danych


Rozszerzamy skrypt z poprzednich zajęć o możliwość dodawania i usuwania rekordów w bazie.

Przypominam, że w poniższym kodzie nie dokonaliśmy jeszcze zabezpieczeń odbieranych z formularza danych.

Należy dokonać dodatkowo walidacji odbieranych danych, która uchroni nas przed atakami SQL Injection, a zapytania do bazy najlepiej opracowywać z wykorzystaniem Prepared Statements, o których trochę niżej.

Do istniejącego formularza dodajemy 2 nowe inputy: Dodai i Usuń, następnie odbieramy dane i dokonujemy na ich podstawie zmian w bazie.

 

<?php

//ustawiamy dane do bazy

$serwer = "localhost";

$user = "root";

$pass = "";

$base = "operacje_db";

$db = @new mysqli($serwer, $user, $pass, $base);

if ($db->connect_error) {

    die('Błąd połączenia z bazą danych: ' . $db->connect_error);

} else {

    echo "Połączenie nawiązano<br>";

    if (!empty($_POST['przycisk'])) { //sprawdzamy czy kliknięto wyszukaj

        $sql = "SELECT * FROM `test` WHERE 1";

        if (!empty($_POST['imie'])) $sql .= " && imie='{$_POST['imie']}'";

        if (!empty($_POST['nazwisko'])) $sql .= " && nazwisko='{$_POST['nazwisko']}'";

        echo $sql . "<br>";

        $wynik = $db->query($sql);

        foreach ($wynik as $row) {

            echo "Witaj {$row['imie']} {$row['nazwisko']}<br>";

        }

    } elseif (!empty($_POST['add'])) { //sprawdzamy czy kliknięto dodaj

        if (!empty($_POST['imie']) && !empty($_POST['nazwisko'])) {

            $sql = "INSERT INTO `test`(`imie`, `nazwisko`) VALUES ('{$_POST['imie']}','{$_POST['nazwisko']}')";

            $wynik = $db->query($sql);

            //echo $db->affected_rows . ' ' . $db->insert_id;

            $ar = $db->affected_rows;

            if ($ar != 0) echo "Dodano {$_POST['imie']} {$_POST['nazwisko']}";

        } else {

            echo "Uzupełnij pola.";

        }

    } elseif (!empty($_POST['del'])) { //sprawdzamy czy kliknięto usuń

        if (!empty($_POST['imie']) && !empty($_POST['nazwisko'])) {

            $sql = "DELETE FROM `test` WHERE imie='{$_POST['imie']}' && nazwisko='{$_POST['nazwisko']}'";

            $wynik = $db->query($sql);

            //echo $db->affected_rows . ' ' . $db->insert_id;

            $ar = $db->affected_rows;

            if ($ar != 0) echo "Usunięto rekordy w ilości: $ar o wartościach: {$_POST['imie']} {$_POST['nazwisko']}";

        } else {

            echo "Uzupełnij pola.";

        }

    } else {

        echo "Wykonaj akcję.";

    }

}

$db->close();

?>

<form action="" method="post">

    Imię: <input type="text" name="imie">

    Nazwisko: <input type="text" name="nazwisko">

    <input type="submit" value="Wyszukaj" name="przycisk">

    <input type="submit" value="Dodaj" name="add">

    <input type="submit" value="Usuń" name="del">

</form>

 

Wykorzystanie „bindowania”, czyli parameterized prepared statements – bezpieczeństwo przede wszystkim

Sparametryzowane zapytania pozwalają na wykonywanie kodu SQL zabezpieczonego przed SQL Injection.

Działa to w ten sposób, że najpierw przygotowujemy zapytanie, czyli do bazy wysyłany jest szablon kwerendy, w którym zamiast zmiennych podajemy znaki zapytania – „?”. Na tej podstawie serwer inicjuje swoje wewnętrzne zasoby do późniejszego wykorzystania.

W kolejnym kroku „bindujemy”, czyli wiązemy ze sobą wartości parametrów (każdemu znakowi zapytania przypisujemy zmienną) i wysyłamy je na serwer.

Na koniec zamykamy parametryzację, by zwolnić zasoby.

Ale dlaczego to takie bezpieczne?

Zbindowane zmienne są wysyłane do serwera niezależnie od zapytania, serwer używa tych wartości tylko w momencie wykonania, po przeanalizowaniu szablonu instrukcji. Parametry nigdy nie są bezpośrednio podstawiane w ciągu zapytania. Należy dostarczyć do serwera wskazówkę dotyczącą typu zmiennej powiązanej, aby utworzyć odpowiednią konwersję.

Przykład podstawowego użycia prepare statements

<?php

//ustawiamy dane do bazy

$serwer = 'localhost'; //lub 127.0.0.1 - to jest nazwa serwera  

$user = 'root'; //użytkownik bazy danych

$pass = ''; //hasło do bazy

$baza = 'operacje_db'; //nazwa bazy

//łączenie z bazą

$db = @new mysqli($serwer, $user, $pass, $baza); //podejście obiektowe - korzystamy z klasy PHP o nazwie mysqli

//sprawdzamy czy się łączy w podejściu obiektowym

if ($db->connect_error) {

    die('Błąd połączenia: ' . $db->connect_error);

} else {

    echo 'Połączenie nawiązano<br />';

    if (!empty($_POST['add'])) {

        $imie = !empty($_POST['imie']) ? $_POST['imie'] : 'nobody';

        $nazwisko = !empty($_POST['nazwisko']) ? $_POST['nazwisko'] : 'nobody';

        //przygotowujemy zapytanie

        $sql = "INSERT INTO `test`(`imie`, `nazwisko`) VALUES (?, ?)";

        $stmt = $db->prepare($sql);

        //bindujemy parametry

        $stmt->bind_param("ss", $imie, $nazwisko); //ss - oznacza string string

        //wykonujemy zapytanie

        $stmt->execute();

        echo "Dodano do bazy: $imie $nazwisko";

    } else {

        echo "Prześlij formularz.";

    }

}

$db->close(); //zamykamy połączenie

?>

<form action="" method="POST">

    Imię: <input type="text" name="imie">

    Nazwisko: <input type="text" name="nazwisko">

    <input type="submit" name="add" value="Dodaj">

</form>

Przykład

<?php

//ustawiamy dane do bazy

$serwer = 'localhost'; //lub 127.0.0.1 - to jest nazwa serwera  

$user = 'root'; //użytkownik bazy danych

$pass = ''; //hasło do bazy

$baza = 'operacje_db'; //nazwa bazy

//łączenie z bazą

$db = @new mysqli($serwer, $user, $pass, $baza); //podejście obiektowe - korzystamy z klasy PHP o nazwie mysqli

//sprawdzamy czy się łączy w podejściu obiektowym

if ($db->connect_error) {

    die('Błąd połączenia: ' . $db->connect_error);

} else {

    $imie = !empty($_POST['imie']) ? htmlspecialchars(stripslashes(trim($_POST['imie']))) : false;

    $nazwisko = !empty($_POST['nazwisko']) ? htmlspecialchars(stripslashes(trim($_POST['nazwisko']))) : false;

    if (!empty($_POST['add'])) {

        if ($imie && $nazwisko) {

            //----------------------------------------------------------------

            //przygotowujemy zapytanie

            $sql = "INSERT INTO `test`(`imie`, `nazwisko`) VALUES (?, ?)";

            $stmt = $db->prepare($sql);

            //bindujemy parametry

            $stmt->bind_param("ss", $imie, $nazwisko); //ss - oznacza string string, drugi parametr musi być zmienną (nie można wpisać bezpośrednio wartości)

            //wykonujemy zapytanie

            $stmt->execute();

            //----------------------------------------------------------------

            /*

            //tak możemy zobaczyć co jest w środku $stmt

            echo '<pre>';

            print_r($stmt);

            echo '</pre>';

            */

            if ($stmt->affected_rows != 0) echo "Dodano do bazy: $imie $nazwisko";

            else echo "Nie dodano wiersza";

        } else {

            echo "Uzupełnij wszystkie pola";

        }

    } elseif (!empty($_POST['search'])) { //w zależności od uzupełnionego pola w formularzu

        if ($imie || $nazwisko) {

            $sql = "SELECT * FROM `test` WHERE 1";

            //przygotowujemy dane do bindowania

            $b_data_types = '';

            $b_values = array();

            if (!empty($imie)) {

                $sql .= " && imie=?";

                $b_data_types .= 's';

                array_push($b_values, $imie);

            }

            if (!empty($nazwisko)) {

                $sql .= " && nazwisko=?";

                $b_data_types .= 's';

                array_push($b_values, $nazwisko);

            }

            //----------------------------------------------------------------

            //przygotowujemy zapytanie

            $stmt = $db->prepare($sql);

            $stmt->bind_param($b_data_types, ...$b_values);

            $stmt->execute();

            $wynik = $stmt->get_result();

            foreach ($wynik as $row) {

                echo "Witaj {$row['imie']} {$row['nazwisko']}<br>";

            }

        } else {

            echo "Wypełnij przynajmniej jedno pole";

        }

    } elseif (!empty($_POST['all'])) { //wyświetl wszystkie

        $sql = "SELECT * FROM `test`";

        //----------------------------------------------------------------

        //przygotowujemy zapytanie

        $stmt = $db->prepare($sql);

        $stmt->execute();

        $wynik = $stmt->get_result();

        foreach ($wynik as $row) {

            echo "Witaj {$row['imie']} {$row['nazwisko']}<br>";

        }

    } else {

        echo "Prześlij formularz.";

    }

}

$db->close(); //zamykamy połączenie

?>

<form action="" method="POST">

    Imię: <input type="text" name="imie">

    Nazwisko: <input type="text" name="nazwisko">

    <input type="submit" name="add" value="Dodaj">

    <input type="submit" name="all" value="Wyświetl wszystko">

    <input type="submit" name="search" value="Wyszukaj">

</form>