php – PDO / prep语句/白名单/设置charset,足够安全,可以防止注入

我正在从扩展 mysql转换为PDO,在阅读了所有我可以从SO和其他地方的大师那里,我有一些残余的怀疑.我想出了以下内容解决典型查询sql注入问题.我只是想知道这是否足够或可能是我在白名单上有点过分,然后我将其复制到我的所有应用程序中.

我不清楚我是否正确地进行了白名单,即如果我也应该以某种方式逃脱.

另外,我不确定是否应该为每个查询将setAttribute模拟为false,或者只为脚本执行一次.

$link = new PDO("MysqL:host=$hostname;dbname=$database;charset=utf8",$username,$password);

$link->setAttribute(PDO::ATTR_EMULATE_PREPARES,false);

            $arr_i=$arr_k='';
            $m_act=$v_act='Y';
            $table = array('prices','versions','models');
            $allowedTables = array('prices','models');             
            $field = array('model_id','version_id','price','models.active','versions.active');
            $allowedFields = array('model_id','versions.active');
            if(count( array_diff($field,$allowedFields))==0 AND  count( array_diff($table,$allowedTables))==0){
            $sql = "SELECT COUNT(disTINCT `" . $field[0] . "`) as ctmod FROM `" . $table[0] . "`
            INNER JOIN `" . $table[1] . "` USING (`" . $field[1] . "`)
            INNER JOIN `" . $table[2] . "` USING (`" . $field[0] . "`)
            WHERE `" . $field[2] . "` BETWEEN :arr_i AND :arr_k
            AND " . $field[3] . " = :m_act
            AND " . $field[4] . " = :v_act";
            $stmt = $link->prepare($sql);
            $stmt->bindParam(':arr_i',$arr_i,PDO::ParaM_INT);
            $stmt->bindParam(':arr_k',$arr_k,PDO::ParaM_INT);
            $stmt->bindParam(':m_act',$m_act,PDO::ParaM_STR);
            $stmt->bindParam(':v_act',$v_act,PDO::ParaM_STR);
            for ($i=0; $i < $ctpri; $i++){
            $k=$i+1;
            $arr_i=$arr_pri[$i]+1;
            $arr_k=$arr_pri[$k];
            $stmt->execute();
            while ($r = $stmt->fetch(PDO::FETCH_ASSOC)) {
            $ctmod[] = $r['ctmod'];
            }
            }
            }
            else{die();}

解决方法

是的,您的代码完全不受sql注入的影响.做得好.

虽然正如@YourCommonSense指出的那样,你所展示的例子中没有理由将表和列名称变成变量.将它们字面地写入查询会更简单.

因此,我假设您提出这个问题,因为您有时会通过应用程序逻辑或变量选择表名和列名,即使您没有在此特定示例中显示它.

我提供的唯一提示是:

>所有字符串连接,结尾双引号,使用.并重新启动双引号会使代码看起来不整洁,并且可能会混淆跟踪您已启动和停止的双引号.变量的PHP字符串插值的另一种风格是用大括号括起来,如下所示:

$sql = "SELECT COUNT(disTINCT `{$field[0]}`) as ctmod FROM `{$table[0]}`
    INNER JOIN `{$table[1]}` USING (`{$field[1]}`)
    INNER JOIN `{$table[2]}` USING (`{$field[0]}`)
    WHERE `{$field[2]}` BETWEEN :arr_i AND :arr_k
    AND `{$field[3]}` = :m_act
    AND `{$field[4]}` = :v_act";

>对于另一种替代方案,您可以使用此处的文档,因此您根本不必担心分隔字符串.如果您的字符串中有文字双引号,那就太好了,因为您不必转义它们:

$sql = <<<GO
    SELECT COUNT(disTINCT `{$field[0]}`) as ctmod FROM `{$table[0]}`
    INNER JOIN `{$table[1]}` USING (`{$field[1]}`)
    INNER JOIN `{$table[2]}` USING (`{$field[0]}`)
    WHERE `{$field[2]}` BETWEEN :arr_i AND :arr_k
    AND `{$field[3]}` = :m_act
    AND `{$field[4]}` = :v_act
GO;

>最后,它与sql注入无关,但一个好的做法是检查prepare()和execute()的返回值,因为如果在解析或执行中发生错误,它们将返回false.

if (($stmt = $link->prepare($sql)) === false) {
    trigger_error(PDO::errorInfo()[2],E_USER_ERROR);
}

(该示例使用PHP 5.4语法取消引用从函数返回的数组.)

或者你可以configure PDO to throw exceptions,所以你不必检查.

$link->setAttribute(PDO::ATTR_ERRMODE,PDO::ERRMODE_EXCEPTION);

相关文章

统一支付是JSAPI/NATIVE/APP各种支付场景下生成支付订单,返...
统一支付是JSAPI/NATIVE/APP各种支付场景下生成支付订单,返...
前言 之前做了微信登录,所以总结一下微信授权登录并获取用户...
FastAdmin是我第一个接触的后台管理系统框架。FastAdmin是一...
之前公司需要一个内部的通讯软件,就叫我做一个。通讯软件嘛...
统一支付是JSAPI/NATIVE/APP各种支付场景下生成支付订单,返...