我一直在阅读有关SQL注入的文章,并决定修改我的代码以防止SQL注入。
例如,我有一个输入,将值插入到数据库中。最初,我的预防注射措施是:
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."; }
在我读过的文章中,有人建议应该使用准备好的语句。我已经更改了代码并使用了它:
$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; }
尽管这可以防止SQL注入,但是它对XSS并没有任何作用。因此,我决定修改test_input(removed $data = mysql_real_escape_string($data);)并使用它来防止脚本注入。
test_input
$data = mysql_real_escape_string($data);
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);
现在,我的问题是关于使用准备好的语句。我将插入三个项目;艺术家,专辑和歌曲。一遍又一遍地重复相同的过程(准备,绑定,执行,关闭)对我来说似乎是多余的。我想创建一个函数,并用它包装准备好的语句过程。像这样的东西:
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(); break; case "INSERT": $result = $query->affected_rows; break; } $query->close(); return $result; }
但是,这带来了一个问题:$vars数组。由于要传递给变量的数量mysqli_stmt::bind_param()是可变的/动态的,因此我在main函数中使用了一个数组p_statement。我不知道如何将数组中的项目传递给mysqli_stmt::bind_param()。bind_param期望(type, var1, var2, varn,),并且我有一个数组。
$vars
mysqli_stmt::bind_param()
p_statement
bind_param
(type, var1, var2, varn,)
我该如何进行这项工作?
您正在寻找implode()
implode()
查看联机帮助页,它显示了有关使用的信息call_user_func_array。我编辑了您的一些片段。
call_user_func_array
function p_statement($mysqli, $query_string = "", $type = "", $vars = []) { $query = $mysqli->prepare($query_string); //assign $type to first index of $vars array_unshift($vars, $type); //Turn all values into reference since call_user_func_array //expects arguments of bind_param to be references //@see mysqli::bind_param() manpage foreach ($vars as $key => $value) { $vars[$key] =& $vars[$key]; } call_user_func_array(array($query, 'bind_param'), $vars); $query->execute(); //INSERT, SELECT, UPDATE and DELETE have each 6 chars, you can //validate it using substr() below for better and faster performance if (strtolower(substr($query_string, 0, 6)) == "select") { $result = $query->get_result(); } else { $result = $query->affected_rows; } $query->close(); return $result; }