串联动态 PDO 查询在没有 bindparam 的情况下可以安全地注入

Concatenated dynamic PDO query safe from injection without bindparam

我有一个生成记录列表的页面,我想根据多种可能性过滤或缩小这些记录的范围。因此,我正在考虑向 SQL 查询添加可能的 "AND" 语句。除了传递给查询的 LIMIT 部分的分页外,用户生成的数据不会进入查询。

我的问题:

  1. 因为我没有使用 bindParam,所以在 LIMIT 之后可以注入吗?

    $start_record = $_GET['page'];
    $ids = array('pid_b', 'pid_l', 'pid_g' );
    $qMarks = str_repeat('?,', count($ids) - 1) . '?';
    
    // THIS MY CONDITION FOR TESTING PURPOSES ONLY
    $testVar = 1;
    
    // NOW WE BUILD THE QUERY
    $sqlQuery = "SELECT * FROM inventory_tbl ";
    $sqlQuery .= "WHERE `consignor_record_id` IN ($qMarks) ';
    $sqlQuery .= ($testVar == 1 ? " AND `inventory_status` <> 'active' " : "");
    $sqlQuery .= "ORDER BY `created_date` DESC LIMIT $start_record, 50 ;";
    
    $productSearch = $dbh->prepare( $sqlQuery );
    

此查询无法安全注入。您可以通过 $_GET['page'].

注入

那么是的,它是开放给sql注入的。一个主要的 sql 注入问题是通过联合将多个查询连接在一起。如果我知道 inventory_tbl 有多少列以及用户 table 等其他名称,我可以发送一个页面获取 0 UNION ALL SELECT col1,col2 FROM USERS -- 的值,它基本上 select库存中的零行 table 和 select 用户 table 或类似的所有行。或者,如果这不起作用,请发送一个子查询以获取将执行其他代码的限制。甚至用分号结束 select 以及除 first.

之外要执行的任何其他查询

来自 PDO Wiki

When in emulation mode (which is on by default), PDO substitutes placeholders with actual data. And with "lazy" binding (using array in execute()), PDO treats every parameter as a string. As a result, the prepared LIMIT ?,? query becomes LIMIT '10', '10' which is invalid syntax that causes the query to fail.

The solutions is turn emulation off (as MySQL can sort all placeholders out properly).

$start_record =  1;//$_GET['page'];
$ids = array('pid_b', 'pid_l', 'pid_g' );
// THIS MY CONDITION FOR TESTING PURPOSES ONLY
$testVar = 1;
//Set up parameters for lazy binding
$params =$ids;//Fill array with ids
array_push($params,$testVar);
array_push($params,$start_record);
$qMarks = str_repeat('?,', count($ids) - 1) . '?';
// NOW WE BUILD THE QUERY
$sqlQuery = "SELECT * FROM inventory_tbl ";
$sqlQuery .= "WHERE `consignor_record_id` IN ($qMarks) ";
$sqlQuery .= "AND $testVar == ? AND `inventory_status` <> 'active' " ;
$sqlQuery .= "ORDER BY `created_date` DESC LIMIT ?, 50 ;";
//To turn emulation off, one can run this code (or set in a connection options array):
$conn->setAttribute( PDO::ATTR_EMULATE_PREPARES, false );
$productSearch = $dbh->prepare($sqlQuery );
$productSearch->execute($params);
//Remove echos after testing
echo $sqlQuery;
echo "<br>";
var_dump($params);

结果

SELECT * FROM inventory_tbl WHERE `consignor_record_id` IN (?,?,?) AND 1 == ? AND `inventory_status` <> 'active' ORDER BY `created_date` DESC LIMIT ?, 50 ;

延迟绑定的参数

  array(5) { [0]=> string(5) "pid_b" [1]=> string(5) "pid_l" [2]=> string(5) "pid_g" [3]=> int(1) [4]=> int(1) }