如何使用动态数量的参数准备语句?

时间:2017-06-13 17:25:14

标签: php mysql security pdo prepared-statement

此代码有效,但由于将GET参数连接到查询中,因此似乎不安全。我正在连接,因为我需要WHERE子句中的动态数量的参数可以是不同类型(IN,正常比较条件)。

如何从动态数量的不同类型的WHERE条件中准备安全语句?

class myclass
{
    public function index($where_clause = 1)
    {
        // db connection (using pdo)
        $stm = $this->dbh->query("SELECT COUNT(amount) paid_qs FROM qanda $where_clause");
        $ret = $stm->fetch(PDO::FETCH_ASSOC);
        // do stuff
    }
    public function gen_where_clause()
    {
        $where_clause = '';
        if (isset($_GET['c']) || isset($_GET['t']))
        {
            $where_clause = 'WHERE ';
            if (isset($_GET['c']))
            {
                $where_clause .= 'cat = ' . $_GET['c'];
            }
            if (isset($_GET['t']))
            {
                if (isset($_GET['c']))
                {
                    $where_clause .= $where_clause . ' AND '
                    }
                $where_clause .= 'tag IN(' . $_GET['t'] . ')';
            }
        }
        return $this->index($where_clause);
    }
}

1 个答案:

答案 0 :(得分:1)

我将在三个方面解决这个问题:代码的实际正确性,解决方案和更好的实践。

代码

此代码实际上工作,因为mentioned存在非常基本的语法错误,甚至会阻止它实际运行。我假设这是一个简化错误,但即使连接也是错误的:每次都重复该语句(。=和字符串本身。这些都将起作用,两者都会破坏查询)

$where_clause .= $where_clause . ' AND '

动态参数数量

具有动态数量的参数的问题很有意思,并且根据需要可能相当复杂,但是在这种情况下,相当简单的参数连接将允许您实现动态数量的参数,如{{3 }}

更准确地说,当一个条件被添加到语句中时,单独将sql添加到语句中,并将值添加到数组中:

$sql .= 'cat = :cat';
$values[':cat'] = $_GET['c'];

然后,您可以准备语句并使用正确的参数执行它。

$stmt = $pdo->prepare($sql);
$stmt->execute($values);

更好的做法

如前所述,这个问题中提供的代码可能根本不起作用,所以让我强调一些基本的OOP原则,这些原则可以大大增强这个代码片段。

  • 依赖注入

db连接应该通过构造函数注入,而不是每次执行查询时都重新创建(如果你在index方法中连接的话)。请注意,$pdo是私有财产。它不应该是公开的,可由其他对象访问。如果这些对象需要数据库连接,那么也在它们的构造函数中注入相同的pdo实例。

class myclass
{
    private $pdo;
    public function __construct(PDO $pdo) { $this->pdo = $pdo; }
}
  • 流程

其中一个方法应该是私有的,由另一个(公共方法)调用,它们将在参数中接收运行函数所需的所有内容。在这种情况下,似乎没有涉及任何参数,一切都来自$_GET

我们可以调整索引,使其接受sql和查询的值,但这三行可以很容易地转移到另一个方法。

private function index($sql, $values)
{
    $stmt = $this->pdo->prepare($sql);
    $stmt->execute($values);
    return $stmt->fetchAll();
}

然后公共gen_where_clause(我相信它被错误地命名......它确实生成值,而不是子句)可以安全使用,这将生成动态数量的参数,保护您免受sql注入。

public function gen_where_clause()
{
    $sql = "SELECT COUNT(amount) AS paid_qs FROM qanda ";
    $values = [];
    if (isset($_GET['c']) || isset($_GET['t']))
    {
        $sql .= ' WHERE ';
        if (isset($_GET['c']))
        {
            $sql .= ' cat = :cat ';
            $values[':cat'] = $_GET['c'];
        }
        // etc.
    }
    return $this->index($sql, $values);
}
  • 过滤输入

对于sql注入保护,即使用参数化查询时,不需要转义值。但是,对输入进行消毒始终是一个正确的想法。在函数之外清理它,然后将其作为参数传递给"搜索"函数,将函数与超全局$_GET解耦。定义过滤的参数超出了本文的相当大的范围,请参考suggested by AbraCadaver

// global code
// create $pdo normally
$instance = new myclass($pdo);
$inputs = filter_input_array(INPUT_GET, $args);
$results = $instance->gen_search_clause($inputs);