如何使此查询注入防范? (PHP)

时间:2012-09-05 17:54:51

标签: php mysql sql pdo sql-injection

要点

我想执行一个SQL查询,该查询依赖于我的GET中可变数量的参数,而不会受到SQL注入的攻击。<​​/ p>

参数

我的网址可以这样形成:

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

目标

有谁知道如何安全有效地做到这一点?

5 个答案:

答案 0 :(得分: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的列表中才能实现这一点。

答案 1 :(得分:0)

白名单是走到这里的方式。如果您只允许已经明确定义的内容,那么您应该没问题。至于效率如何,这都是相对的。您使用的版本对于相对较小的列表(例如列数低于100的列表)表现良好,所以我不担心。

使用PDO的加分点。

有可能您对“允许”列的定义以及数据库中的实际内容可能会有所不同。更宽松的规范可能是使用SHOW FIELDS为相关表格获取字段,并且仅允许这些字段。

答案 2 :(得分:0)

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

答案 3 :(得分: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();

答案 4 :(得分:0)

PDO::prepare会帮助你。这正是专家建议的。切勿使用mysql_real_escape_string (string)。总是去准备好的陈述。