要点
我想执行一个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");
目标
有谁知道如何安全有效地做到这一点?
答案 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)
。总是去准备好的陈述。