我刚刚开始研究我的第一个项目(为了好玩)。我正在学习PHP和MySQL,并完成了我的第一个工作应用程序。它有效,但我现在正在学习如何保护我的应用程序,从而防止SQL注入。 我有大约50多个PHP文件来管理与MySQL数据库的交互。他们都看起来像这样:
<?php
$inputvalues = $_POST;
$errors = false;
$result = false;
session_start();
$uid = $_SESSION['usr_id'];
$mysqli = new mysqli('localhost', "root", "", "testdb");
if (mysqli_connect_errno()) {
printf("Connect failed: %s\n", mysqli_connect_error());
exit();
}
foreach ($inputvalues as $key => $value) {
if(isset($value) && !empty($value)) {
$inputvalues[$key] = $mysqli->real_escape_string( $value );
} else {
$errors[$key] = 'The field '.$key.' is empty';
}
}
if( !$errors ) {
$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_close($mysqli);
echo json_encode(['result' => $returnResult, 'errors' => $errors]);
exit;
?>
这是我在整个应用程序中用于向/从数据库读取和写入数据的格式。如果我需要将它们更改为准备好的语句,我没有插入任何信息而只是检索它们,我将如何处理它?</ p>
另外,如果我没有向数据库输入任何数据,是否仍然容易受到注入?
请您提供一个示例,说明如何使我的当前代码适应准备好的语句,我真的很感激。
答案 0 :(得分:6)
我一直处于同样的境地。我也在使用连接语句,然后将应用程序切换到预备语句。
坏消息您是要更改通过将客户端数据连接到SQL语句而构建的每个SQL语句,这几乎就是您的50个源文件中的每个SQL语句。
好消息是转换为准备好的陈述所获得的收益是无价的,例如:
1 - 你永远不会担心所谓的#SQL注入攻击&#34;
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";
这是在使用准备好的语句之前必须要做的事情,这使你有可能忘记将一个参数作为普通人逃脱。并且攻击者破坏你的系统只需要1个未转义的参数。
通常更改源文件总是存在风险并且很痛苦,特别是如果您的软件设计不好并且您没有明显的测试计划。但我会告诉你我做了什么让它变得越来越容易。
我创建了一个每个数据库交互代码都会使用的函数,所以你可以在以后的某个地方改变你想要的东西 - 这个函数 - 你可以做这样的事情
class SystemModel
{
/**
* @param string $query
* @param string $types
* @param array $vars
* @param \mysqli $conn
* @return boolean|$stmt
*/
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;
}
/* used only inside preparedQuery */
/* code taken from: https://stackoverflow.com/a/13572647/5407848 */
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 Injection攻击。它是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注入攻击,这是值得花费更改源文件的费用