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

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

提问人: 提问时间:10/27/2019 最后编辑:Dharman 更新时间:10/27/2019 访问量:74

问:

我有两个易受 sql 注入影响的函数,我已经设法将第一个函数转换为准备好的函数,但我不明白如何转换第二个函数。这是原始的第一个:

    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);
    }

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

php mysqli 准备语句 sql 注入

评论

0赞 Your Common Sense 10/27/2019
你的具体问题是什么?为什么最后不能像在其他函数中一样启动 - 使用...$value的东西?
0赞 10/27/2019
@YourCommonSense 我确实弄清楚了那部分,就像我用我没有管理过并且我仍然不清楚的阵列来做那部分一样。$rows
0赞 Egret 10/29/2019
请注意,如果 SQL 本身可以被篡改,这仍然容易受到 SQL 注入的影响。将其移动到预准备语句可以保护变量的注入,但您仍然需要确保 SQL 是可信的。
0赞 10/29/2019
@Egret你能举一个小例子来说明你的意思吗?
0赞 Egret 10/29/2019
sql 本身是作为变量传入的,因此我们无法从代码片段中看出它的源是什么。如果它是从不受信任的来源(客户端)传入的,或者它是通过与来自不受信任的来源的字符串串联创建的,这仍然会使您容易受到 SQL inj 的攻击 - 即使您使用准备好的语句。

答:

1赞 Your Common Sense 10/27/2019 #1

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

其他几点需要注意

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

鉴于我们可以根据我的 Mysqli 辅助函数创建一个易于使用和阅读的过程:

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
}
0赞 Nick 10/27/2019 #2

如果您已经安装(以允许使用 mysqli_stmt::get_result),则更改可能相当简单。第一部分与 相同,然后我们只使用 调用 ,函数的其余部分保持不变:mysqlndpreparedModifyget_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;
}

如前所述,您不需要循环来生成,您可以简单地使用:$rowsmysqli_result::fetch_all

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

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

评论

0赞 10/27/2019
请问您为什么选择使用而不是?num_rowsaffected_rows
1赞 Nick 10/27/2019
@Doesitmatter 用于 和 查询。 用于查询。affected_rowsINSERTUPDATEDELETEnum_rowsSELECT
1赞 Dharman 10/27/2019
您不需要 while 循环。您可以只获取所有记录并返回。
1赞 Nick 10/27/2019
@Doesitmatter查询不应该有子句...这就是失败的原因。SELECTVALUESprepare
1赞 Nick 10/27/2019
@Doesitmatter REGEXP 表达式占位符也不应放在引号中。