当前位置: 首页 > 面试题库 >

切换到准备好的语句

呼延才俊
2023-03-14
问题内容

我刚刚开始做我的第一个项目(很有趣)。我正在学习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;
?>

这是我在整个应用程序中用于向数据库读写数据的格式。如果我需要将它们更改为准备好的语句,而不是在其中插入任何信息,而只是在检索它们,该如何处理?

另外,如果我没有向数据库输入任何数据,它仍然容易受到注入的攻击吗?

您能否为我提供一个示例,说明如何使当前代码适应准备好的语句,对此我将不胜感激。


问题答案:

我一直处于同样的情况。我也使用了串联语句,然后将应用程序切换为准备好的语句。

坏消息 是,您将更改通过将客户端数据连接到SQL语句而构建的每个SQL语句,这几乎将是50个源文件中包含的每个SQL语句。

好消息 是从切换到准备好的语句所获得的收益是无价的,例如:

1-您永远不会担心“ SQL注入攻击”

PHP 手册说

如果应用程序仅使用准备好的语句, 则开发人员可以确保不会发生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攻击。往哪里去是INSERTSELECTDELETEUPDATE。例如

$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注入攻击,这值得您更改源文件。



 类似资料:
  • 我有以下准备的一个准备好的声明失败了... 我在我的第一份准备好的声明中几乎有同样的准备,而且它进行得很好。我不确定是否我有两个准备好的声明,如果这是造成一个问题,或情况可能是什么? 现在我有个错误... 更新-可能的内部连接,像这样?

  • 场景:继我之前的问题(在存储过程中使用游标循环行MySQL)之后,我尝试执行一个嵌套的prepare语句,在该语句中,我向外部语句输入一个日期,然后调用内部语句,该语句从表中获取数据。 代码: 问题:这段代码运行时没有错误,但没有给出任何结果。如果我只运行第一个准备好的语句,一个接一个地直接输入变量,就可以了。所以我猜问题在于我的第二句话。 问题:你知道我做错了什么吗? Obs:第二个代码应该循环

  • 问题内容: 我一直在做的简单连接,: 虽然使用这个我一直使用的简单的方法,使查询之前逃脱的任何数据,不管是,,或者通过使用 现在我知道这在一定程度上是安全的! 它逃脱了危险人物;但是,它仍然容易受到其他攻击的攻击,这些攻击可能包含安全字符,但可能有害于显示数据或在某些情况下恶意修改或删除数据。 因此,我进行了一些搜索,以了解有关PDO,MySQLi和准备好的语句的信息。是的,我可能迟到了游戏,但是

  • 其思想是创建一个准备好的语句将信息插入到用户表中,然后从生成的内容中获取insert_id以在另一个insert语句中使用 这是我的注册脚本的一个版本 第一条准备好的语句正确运行并向表中插入信息,之后成功回显$RETURNID变量。脚本中的下一个是我准备好的第二条语句,当它试图运行im时,得到的错误是: 致命错误:对第17行d:\filepath\register.php中的非对象调用成员函数bi

  • 问题内容: 在将HQL转换为SQL时,Hibernate在JDBC内部使用PreparedStatement。HQL中的内联参数如何处理? 例: 将状态“解析”并用作SQL中的参数,或者将其作为内联参数发送。 我的观点背后的原因是“最佳做法”,以及针对重复调用的查询性能 问题答案: 它被内联发送。当是客户端控制的值时,您绝对不希望这样做。 而是将其参数化: 也可以看看: OWASP-hiberna