我曾处于同样的情况。我也使用连接语句,后来我将应用程序切换到了预处理语句。
“坏消息”是你需要更改通过连接客户端数据到SQL语句中构建的每个SQL语句,几乎是你50个源文件中的每个SQL语句。
“好消息”是切换到预处理语句所获得的收益是无价的,例如:
1-你永远不必担心所谓的“SQL注入攻击”
PHP
manual说:
如果应用程序完全使用预处理语句,“开发人员可以确保不会发生SQL注入”(但是,如果正在使用未经转义的输入构建查询的其他部分,则仍然可能发生SQL注入)。
对我来说,这个原因-安心-足以支付更改我的源代码的成本。现在你的客户可以在表单名称字段中键入
robert; DROP table students; -- ;)
,而你可以放心,什么都不会发生。
2- 您不再需要转义客户端参数。您可以直接在SQL语句中使用它们,例如:
$query = "SELECT FROM user WHERE id = ?";
$vars[] = $_POST['id'];
替代
$id = $mysqli->real_escape_string($_POST['id']);
$query = "SELECT FROM user WHERE id = $id";
在使用预处理语句之前,你必须做的是将所有参数进行转义,否则有可能会忘记对某个参数进行转义,这会使你的系统面临被攻击者破坏的风险,只要有一个未经转义的参数就足以导致此情况发生。
改变代码
通常更改源文件总是存在风险和痛苦的,特别是如果您的软件设计不好且没有明显的测试计划。但我会告诉您,我做了什么来尽可能地简化它。
我制作了一个函数,每个数据库交互代码都将使用它,因此您可以在一个地方随意更改 -那个函数- 您可以像这样制作:
class SystemModel
{
public function preparedQuery($query,$types, array $vars, $conn)
{
if (count($vars) > 0) {
$hasVars = true;
}
array_unshift($vars, $types);
$stmt = $conn->prepare($query);
if (! $stmt) {
return false;
}
if (isset($hasVars)) {
if (! call_user_func_array(array( $stmt, 'bind_param'), $this->refValues($vars))) {
return false;
}
}
$stmt->execute();
return $stmt;
}
protected function refValues($arr)
{
if (strnatcmp(phpversion(), '5.3') >= 0) {
$refs = array();
foreach ($arr as $key => $value)
$refs[$key] = &$arr[$key];
return $refs;
}
return $arr;
}
}
现在,你可以在任何你想要的源文件中使用这个接口,例如,让我们修改你在问题中提供的当前SQL语句。让我们改变这个:
$mysqli = new mysqli('localhost', "root", "", "testdb");
$addresult = "
SELECT a.firstnames, a.surname, a.schoolrole, a.datejoined
FROM teachers a LEFT JOIN schools b ON a.schoolid = b.id
WHERE b.id = '".$inputvalues['schoolid']."'";
if( $result = $mysqli->query($addresult) ) {
while($row = $result->fetch_all())
{
$returnResult = $row;
}
}
进入此
$mysqli = new mysqli('localhost', "root", "", "testdb");
$sysModel = new SystemModel();
$addresult = "
SELECT a.firstnames, a.surname, a.schoolrole, a.datejoined
FROM teachers a LEFT JOIN schools b ON a.schoolid = b.id
WHERE b.id = ?";
$types = "i"; // for more information on paramters types, please check :
//https://php.net/manual/en/mysqli-stmt.bind-param.php
$vars = [];
$vars[] = $inputvalues['schoolid'];
$stmt = $sysModel->preparedQuery($addresult, $types, $vars, $mysqli);
if (!$stmt || $stmt->errno) {
die('error'); // TODO: change later for a better illustrative output
}
$result = $stmt->get_result();
$returnResult = [];
while ($row = $result->fetch_array(MYSQLI_ASSOC)) {
$returnResult[] = $row;
}
如果我没有向数据库输入任何数据,它仍然容易受到注入攻击吗?
是的,SQL注入攻击是通过将恶意字符串连接到您的SQL语句中来实现的,无论是INSERT、SELECT、DELETE还是UPDATE。例如:
$query = "SELECT * FROM user WHERE name = '{$_GET['name']}' AND password = '{$_GET['pass']}'"
这样的东西可能会被利用。
// exmaple.com?name=me&pass=1' OR 1=1; --
这将导致一条SQL语句
$query = "SELECT * FROM user WHERE name = 'me' AND password = '1' OR 1=1; -- '"
//executing the SQL statement and getting the result
if($result->num_rows){
//user is authentic
}else{
//wrong password
}
// that SQL will always get results from the table which will be considered a correct password
祝你成功地将软件转换为预处理语句,并记住,从知道无论发生什么情况,你都可以安全地免受SQL注入攻击的安心感中获得的价值,是改变源文件成本所值得的。