我如何将此 sql-injection-susceptible 函数转换为 MySQLi 中的准备函数?

How do I translate this sql-injection-susceptible function to a prepared function in MySQLi?

我有两个函数是 sql-injection-susceptible,我已经设法将第一个函数转换为准备好的函数,但我看不出如何转换第二个函数。这是原来的第一个:

    function modify($sql, &$id)
    {
        $link = database_link();

        $result = mysqli_query($link, $sql);

        $insertId = mysqli_insert_id($link);

        return mysqli_affected_rows($link);
    }

我翻译成:

    function preparedModify($sql, $types, &$insertId, ...$value)
    {
        $statement = mysqli_prepare(database_link(), $sql);

        $statement->bind_param($types, ...$value);

        $statement->execute();

        $insertId = $statement->insert_id;

        return $statement->affected_rows;
    }

效果不错,我很满意。这是我的第二个需要翻译的函数:

    function select($sql, &$rows)
    {

        $link = database_link();

        $result = mysqli_query($link, $sql);

        $rows = array();

        while ($row = mysqli_fetch_array($result, MYSQLI_ASSOC)) {
            $rows[] = $row;
        }

        return mysqli_num_rows($result);
    }

作为初学者,我完全不清楚如何翻译它。您能否在帮助我的同时分解您的答案以指出您的翻译背后的策略是什么?因为我对第一个功能的策略只是在谷歌上搜索替代功能,所以我设法完成了第一个功能,但是使用相同的方法我对第二个功能一无所获。

有一个怪癖,我不得不承认,使用 运行ning 准备好的 SELECT 查询,因为你不能直接从语句中获取熟悉的数组,所以你需要额外的函数调用, get_result()。但至少你可以以与其他函数相同的方式开始,因为它是 运行 带有准备好的语句的函数的正确方法。

其他一些注意事项

  • 我想 database_link() 每次调用时都会创建一个新的数据库连接。永远不应该这样,必须只创建一次连接。因此,首先创建它,然后将 $link 变量传递到所有函数调用中。
  • return通过参数获取函数的结果是丑陋且不可读的,因此不受欢迎。而且您不必 return 行计数 - 它只是没有意义,因为您始终可以使用 count($rows) 来获取计数。
  • 将单独的变量放入函数调用中非常不方便,我很难学到的。相反,将它们放在数组的形式中。这样,您将能够拥有单独的变量或已经包含所有所需数据的单个变量。

基于我的 Mysqli helper function:

,鉴于所有这些,我们可以创建一个易于使用和阅读的过程
function select($link, $sql, $values = [], $types = '')
{
    if (!$values) {
        $result = $link->query($sql);
    } else {
        $types = $types ?: str_repeat("s", count($values));
        $stmt = $mysqli->prepare($sql);
        $stmt->bind_param($types, ...$values);
        $stmt->execute();
        $result = $stmt->get_result();
    }
    return $result->fetch_all(MYSQLI_ASSOC);   
}

$link = database_link();
$rows = select($link, "SELECT * FROM employees WHERE salary > ?", [$gross]);
if ($rows) {
    // you don't actually need even a count() call
}

如果您安装了 mysqlnd(以允许使用 mysqli_stmt::get_result),更改会相当简单。第一部分与 preparedModify 相同,然后我们只调用 get_result 函数的其余部分保持不变:

function preparedSelect($sql, $types, &$rows, ...$value)
{
    $statement = mysqli_prepare(database_link(), $sql);

    $statement->bind_param($types, ...$value);

    $statement->execute();

    $result = $statement->get_result();

    $rows = array();

    while ($row = mysqli_fetch_array($result, MYSQLI_ASSOC)) {
        $rows[] = $row;
    }

    return $statement->num_rows;
}

如前所述,您不需要循环生成 $rows,您只需使用 mysqli_result::fetch_all:

$rows = $result->fetch_all(MYSQLI_ASSOC);

同样,这需要安装本机 mysqlnd 驱动程序。