3

要点

我想执行一个 SQL 查询,该查询依赖于我的 GET 中可变数量的参数,而不容易受到 SQL 注入的影响。

参数

我的 URL 可以这样形成:

https://www.example.com/index.php?param1=blah1,param2=blah2,param3=a,b,c

或像这样:

https://www.example.com/index.php?param1=blah1,param2=blah2,param3=a,b,c,d,e,f,g

换句话说,param3 可以有可变数量的逗号分隔参数 a、b、c 等。

白名单

我检查以确保 a、b、c 等中的所有参数。在我执行查询之前已在批准的白名单中。

// $valid_params is an array of pre-approved parameters.
$arr = explode(',', clean($_GET['param3']));
$params = Array();
foreach($arr as $param){
  if(in_array($param, $valid_params)){
    array_push($params, $param);
  }
}

查询

我这样设置我的数据库连接(使用 MySQL):

$db_connection = new PDO("mysql:host={$DB_HOST};dbname={$DB_NAME}",$DB_USER,$DB_PASS);
$db_connection->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
$db_connection->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

我想执行这样的查询(安全除外):

$comma_separated_params = implode(',',$params);
$result = $db_connection->query("SELECT {$comma_separated_params} FROM some_table");

目标

有谁知道我怎样才能安全有效地做到这一点?

4

5 回答 5

1

根据您对开销的关注,您可以SELECT *在 PHP 中过滤数组 - 如果参数从未发送到数据库,则没有注入空间。

然而,这并不是最优雅的解决方案。这是我的做法:

$comma_separated_params =
  implode(
    ",",
    array_map(
      function($a) {return "`".$a."`";},
      array_intersect(
        explode(",",$_GET['param3']),
        $valid_params
      )
    )
  )
);

单行奇观(为清楚起见添加了换行符)将获取$_GET['param3']变量,用逗号分隔,将其与您的有效参数(而不是foreach循环)相交,将每个元素包装在反引号中(请参见下面的注释)并最后粘合它们和逗号一起。

看,反引号允许您使用任何字符串作为字段名称。通常是允许关键字作为名称,但也可以允许带有空格的列名,等等。在反引号中唯一有意义的字符是反斜杠和反引号 - 可以安全地假设它们不存在,因为它们必须在您的列表中$valid_params才能达到这一点。

于 2012-09-05T18:04:19.587 回答
0

白名单是这里的方法。如果你只允许你已经明确定义的东西,你应该没问题。至于效率如何,这都是相对的。您使用的版本对于相对较小的列表(例如少于 100 列的列表)表现良好,所以我不会担心。

使用 PDO 的奖励积分。

您对“允许”列的定义与数据库中的实际内容可能会有所不同。一个更宽松的规范可能是获取SHOW FIELDS用于相关表的字段并只允许这些字段。

于 2012-09-05T18:01:29.690 回答
0

如果您只允许在参数 3 中传递预定义值的特定列表,并且您正在将输入值与它们进行比较,我认为您没有任何注入暴露,因为您可以完全控制最终的值进入你的$comma_seperated_params变量。

于 2012-09-05T18:01:45.617 回答
0

这需要一些工作才能完成,但使用参数绑定它看起来像这样:

$binding = array();
$selects = array();
foreach ( $params as $value ) {
  $binding[] = ':' . $value; 
  $selects = '?';
}

$select = implode(',', $select);

$result = $db_connection->prepare("SELECT $select FROM some_table"); 
foreach ( $binding as $key => $bind ) {
  $result->bindParam($key, $bind, PDO::PARAM_STR);
}

$result->execute();
于 2012-09-05T18:11:01.083 回答
0

PDO::prepare会帮助你。这正是专家推荐的。永远不要使用mysql_real_escape_string (string). 总是去准备好的陈述。

于 2012-09-06T11:52:49.720 回答