Checkmarx二阶SQL注入php

Checkmarx second order SQL Injection php

提问人:fahad shaikh 提问时间:3/30/2023 最后编辑:baruchirofahad shaikh 更新时间:4/1/2023 访问量:248

问:

我有一个自定义函数,可以创建准备好的语句。由于我使用的是预处理语句,因此checkmarx不应在此函数中突出显示二阶SQL注入问题。

function insertUsingPreparedStmt($table,  $params, $bind_condition)
{

    $dbobj = $this->getMysqliObject();

    $fields             = array_keys($params);
    $values             = array_values($params);
    $question_mark      = '';

    foreach ($values as $id)  $question_mark .= '?, ';
    $question_mark      = rtrim($question_mark, ', ');

    $bind_params[] = &$bind_condition['bind_type'];
    $n = count($values);

    for ($i = 0; $i < $n; $i++) {
        $bind_params[] = &$values[$i];
    }


    $query = "INSERT INTO $table (" . implode(" , ", $fields) . ") VALUES ( $question_mark )";

    $stmt = $dbobj->prepare($query);
    call_user_func_array(array($stmt, 'bind_param'), $bind_params);
    $stmt->execute();
    $stmt->close();
}

Checkmarx 说

第 994 行的方法从元素中获取数据库数据。然后,此元素的值在未经过适当审查或验证的情况经代码,并最终用于 的 方法中 数据库查询。这可能会引发二阶 SQL 注入攻击。executeStoreProcedureapp/Mysqlim.phpfetch_objectinsertUsingPreparedStmtapp/Mysqlim.php

大约有 12 个这样的问题,我敢肯定这些都是误报。我怎样才能从checkmarx中摆脱这些?

准备语句 SQL-注入 Checkmarx

评论

0赞 Dharman 3/30/2023
这很可能是在抱怨,因为我认为这些值没有根据白名单进行检查。$fields = array_keys($params)

答:

1赞 Bill Karwin 3/31/2023 #1

the 和 the 都是 SQL 注入的可能向量,因为它们在未经验证的情况下入到 SQL 查询中。$tablekeys($params)

根据错误,我猜一个或两个最初来自数据库,因此使其成为二阶 SQL 注入。如果有人可以通过插入一个字符串来毒害你的数据库,然后查询该字符串并最终用作表名或列名,那么这就是 SQL 注入。

你经常听到人们说“使用准备好的声明!”,好像这是关键部分。事实上,如果查询受到 SQL 注入的影响,简单地使用并不能神奇地使查询安全。正是这些参数使查询变得安全。但是,在 SQL 查询中,只能使用参数代替标量值,而不能使用表标识符或列标识符。那么如何解决这个问题呢?prepare()

一个好的解决方案是使用允许列表来验证标识符。如果插值是代码中的固定值,而不是不安全的源(如用户输入)或来自外部源(如数据库、文件或 Web 请求),则该值是安全的。外部输入可以选择使用哪个值,但值本身不是外部输入。

假设您的 PHP 应用程序中有一个名为 的全局变量。假设它是一个哈希值,其中表名是键,而这个键指向另一个哈希值,其中该表的列名是键。下面是单个表的示例:$allMyTables

$allMyTables = [
  'mytable' => [
    'id' => true,
    'name' => true,
    'score' => true,
    'created_at' => true,
    'updated_at' => true
  ]
];

你如何填充它现在并不重要。假设您只是将其作为静态 PHP 文件添加到项目中。如果添加表或列,则需要编辑此 PHP 文件。关键是这些值在代码中是固定的,它们不是来自外部源。

以下是我编写函数的方式:

function insertUsingPreparedStmt($table,  $params)
{
    global $allMyTables;

    /* first validate that the table exists */
    if (!array_key_exists($table, $allMyTables)) {
        throw new ErrorException("Table $table is unknown. Contact site administrator.");
    }

    /* get the subset of $params for columns known to belong to that table */
    $params = array_intersect_key($params, $allMyTables[$table]);

    $dbobj = $this->getMysqliObject();

    $columns            = array_keys($params);
    $columns_string     = implode(',', array_map(func($col) { return "`$col`"; }, $columns));
    $values             = array_values($params);
    $placeholders       = implode(',', array_fill(0, count($columns), '?'));

    $query = "INSERT INTO `$table` ($columns_string) VALUES ($placeholders)";

    $stmt = $dbobj->prepare($query);
    $stmt->bind_param(str_repeat('s', count($columns)), ...$values);
    $stmt->execute();
    $stmt->close();
}

在此示例中,我还确保将表名和列名放在反引号内,以防它们与 SQL 保留关键字冲突。

在此函数中,以及在大多数情况下,在MySQL中,您可以将所有参数作为字符串类型传递。MySQL将根据需要进行类型转换。只有少数例外。

PHP 将数组传递给可变参数函数的语法从 PHP 5.6(大约 2014 年)开始就已经可用。不再需要用于此类情况。...call_user_func_array()

实际上,如果我编写这个函数,我会进行另一个更改:我将使用 PDO 而不是 mysqli。它使一些任务更简单。该函数的最后一行是:

    $stmt = $dbobj->prepare($query);
    $stmt->execute($values);