为什么不能将表名传递给准备好的PDO语句?

$stmt = $dbh->prepare('SELECT * FROM :table WHERE 1');
if ($stmt->execute(array(':table' => 'users'))) {
    var_dump($stmt->fetchAll());
}

是否有另一种安全的方法将表名插入SQL查询?有了安全,我的意思是我不想做

$sql = "SELECT * FROM $table WHERE 1"

当前回答

我看到这是一个旧帖子,但我发现它很有用,我想分享一个类似于@kzqai建议的解决方案:

我有一个函数,它接收两个参数,比如…

function getTableInfo($inTableName, $inColumnName) {
    ....
}

在里面,我检查我所设置的数组,以确保只有具有“blessed”表的表和列是可访问的:

$allowed_tables_array = array('tblTheTable');
$allowed_columns_array['tblTheTable'] = array('the_col_to_check');

然后运行PDO之前的PHP检查看起来像…

if(in_array($inTableName, $allowed_tables_array) && in_array($inColumnName,$allowed_columns_array[$inTableName]))
{
    $sql = "SELECT $inColumnName AS columnInfo
            FROM $inTableName";
    $stmt = $pdo->prepare($sql); 
    $stmt->execute();
    $result = $stmt->fetchAll(PDO::FETCH_ASSOC);
}

其他回答

简短的回答是否定的,你不能在PDO的Prepared execute语句中使用动态表名、字段名等,因为它会向它们添加引号,这会中断查询。但是如果你可以净化它们,那么你就可以安全地把它们放在查询本身中,就像你使用MySQLi一样。

正确的方法是使用mysqli的mysqli_real_escape_string()函数,因为mysql_real_escape_string被匆忙地从PHP中删除,而没有考虑它如何影响动态结构应用程序。

$unsanitized_table_name = "users' OR '1'='1"; //SQL Injection attempt
$sanitized_table_name = sanitize_input($unsanitized_table_name);

$stmt = $dbh->prepare("SELECT * FROM {$unsanitized_table_name} WHERE 1"); //<--- REALLY bad idea
$stmt = $dbh->prepare("SELECT * FROM {$sanitized_table_name} WHERE 1"); //<--- Not ideal but hey, at least you're safe.

//PDO Cant sanitize everything so we limp along with mysqli instead
function sanitize_input($string)
{
   $mysqli = new mysqli("localhost","UsahName","Passerrrd");
   $string = $mysqli->real_escape_string($string);

   return $string;
}

使用前者本质上并不比后者更安全,您需要清除输入,无论它是参数数组的一部分还是简单变量的一部分。因此,我不认为对$table使用后一种形式有任何错误,只要您在使用它之前确保$table的内容是安全的(字母加下划线?)。

在PDO中,表名和列名不能被参数替换。

在这种情况下,您只需要手动过滤和清除数据。一种方法是将简写参数传递给动态执行查询的函数,然后使用switch()语句创建用于表名或列名的有效值白列表。这样用户输入就不会直接进入查询。例如:

function buildQuery( $get_var ) 
{
    switch($get_var)
    {
        case 1:
            $tbl = 'users';
            break;
    }

    $sql = "SELECT * FROM $tbl";
}

通过不保留默认大小写或使用返回错误消息的默认大小写,可以确保只使用您希望使用的值。

我想知道你是否可以提供你自己的自定义消毒功能,就像这样简单:

$value = preg_replace('/[^a-zA-Z_]*/', '', $value);

我还没有真正想过,但似乎除了字符和下划线之外,删除任何东西都可以。

我看到这是一个旧帖子,但我发现它很有用,我想分享一个类似于@kzqai建议的解决方案:

我有一个函数,它接收两个参数,比如…

function getTableInfo($inTableName, $inColumnName) {
    ....
}

在里面,我检查我所设置的数组,以确保只有具有“blessed”表的表和列是可访问的:

$allowed_tables_array = array('tblTheTable');
$allowed_columns_array['tblTheTable'] = array('the_col_to_check');

然后运行PDO之前的PHP检查看起来像…

if(in_array($inTableName, $allowed_tables_array) && in_array($inColumnName,$allowed_columns_array[$inTableName]))
{
    $sql = "SELECT $inColumnName AS columnInfo
            FROM $inTableName";
    $stmt = $pdo->prepare($sql); 
    $stmt->execute();
    $result = $stmt->fetchAll(PDO::FETCH_ASSOC);
}