samedi 25 juin 2016

Wrapping a prepared statement in a function

I've been reading articles about SQL Injection, and decided to modify my code to prevent SQL injection.

For example, I have an input which I insert the value to my database. Initially, my guard against injection was this:

function test_input($data) {
    $data = trim($data);
    $data = stripslashes($data);
    $data = htmlspecialchars($data);
    // $data = addslashes($data);
    $data = mysql_real_escape_string($data);
    return $data;
}

$artist = $_POST["artist"];     // can be anything
$artist = test_input($artist);  // escaped chars are &, quotes, <, >, n, r, etc.

if ($mysqli->query("SELECT * FROM `my_table` WHERE `artist` = '$artist'")->num_rows == 0) {
    $mysqli->query("INSERT INTO my_table (artist) VALUES ('$artist')");
    echo "New artist is added.";
} else {
    echo "Artist already exists.";
}

In the articles I've read, it was suggested that one should use prepared statements. I've changed my code and used that:

$artist = $_POST["artist"]; // can be anything

$query = $mysqli->prepare("SELECT * FROM my_table WHERE artist = ?");
$query->bind_param("s", $artist);
$query->execute();
$result = $query->get_result();
$query->close();

if ($result->num_rows == 0) {
    echo "Artist doesn't exist in the DB." . PHP_EOL;
    $query = $mysqli->prepare("INSERT INTO my_table (artist) VALUES (?)");
    $query->bind_param("s", $artist);
    $query->execute();
    if ($query->affected_rows > 0) {
        echo "Artist is added to the DB." . PHP_EOL;
    }
    $query->close();
} else {
    echo "Artist already exists in the DB." . PHP_EOL;
}

While this prevents SQL injection, it doesn't do anything about XSS. So I decided to modify test_input (removed $data = mysql_real_escape_string($data);) and use it to prevent script injection.

function test_input($data) {
    $data = trim($data);
    $data = stripslashes($data);
    $data = htmlspecialchars($data);
    return $data;
}

$artist = $_POST["artist"]; // can be anything
$artist = test_input($artist);

Now, my problem is about using prepared statements. I'll be inserting three items; artist, album, and song. Repeating the same process (prepare, bind, execute, close) over and over again seems redundent to me. I want to create a function and wrap the prepared statement process with it. Something like this:

function p_statement($mysqli, $query_string = "", $type = "", $vars = []) {
    $query = $mysqli->prepare($query_string);
    $query->bind_param($type, $vars);
    $query->execute();
    $result = null;
    preg_match("/^[A-Z]+/", $query_string, $command);
    switch ($command[0]) {
        case "SELECT":
            $result = $query->get_result();
        case "INSERT":
            $result = $query->affected_rows;
    }
    $query->close();
    return $result;
}

Though, this presents a problem: $vars array. Since the number of variables that'll be passed to mysqli_stmt::bind_param() will be variable/dynamic, I've used an array in the main function p_statement. I don't know how I should the pass the items in the array to the mysqli_stmt::bind_param(). bind_param expects (type, var1, var2, varn,), and I've got an array.

How can I make this work?

Aucun commentaire:

Enregistrer un commentaire