这种动态SQL查询生成是否可以安全地进行注入?

时间:2016-07-08 17:28:04

标签: php mysql security sql-injection

在我的脚本中是否有某些东西可能会逃脱卫生设施,或者大多数 SQL注入是否安全?我理解它的方式,如果你将查询作为准备好的参数传递,那么构建查询的方式并不重要,对吗?

Edit2:我编辑了代码以反映绑定$ _POST值的建议

$q = $pdo->prepare('SHOW COLUMNS FROM my_table');
$q->execute();
$data = $q->fetchAll(PDO::FETCH_ASSOC);
$key = array();
foreach ($data as $word){
    array_push($key,$word['Field']);
    } 
$sqlSub= "INSERT INTO other_table(";
$n = 0;
foreach ($key as $index){
    $sqlSub = $sqlSub.$index.", ";
    $n = $n + 1;
}
$sqlSub = $sqlSub.") VALUES (";
for ($i=1; $i<$n;$i++){
    $sqlSub = $sqlSub."?, ";
}
$sqlSub = $sqlSub.."?)";
$keyValues = array();
for($i=0;i<n;$i++){
    array_push($keyValues,$_POST[$key[$i]]);
}
$q->$pdo->prepare($sqlSub);
q->execute($keyValues);

编辑:这是建议编辑后最终查询的样子

INSERT INTO other_table($key[0],...,$key[n]) VALUES (?,...,nth-?);

3 个答案:

答案 0 :(得分:0)

没有。显示的示例代码是大多数SQL注入的安全。

你理解是完全错误的。

重要的是SQL文本。如果使用可能不安全的值动态生成,则SQL文本容易受到攻击。

代码在多个地方容易受到攻击。甚至列的名称也可能不安全。

CREATE TABLE foo
( `Robert'; DROP TABLE Students; --`  VARCHAR(2)
, `O``Reilly`                         VARCHAR(2)
);

SHOW COLUMNS FROM foo

FIELD                             TYPE        NULL
--------------------------------  ----------  ----
Robert'; DROP TABLE Students; --  varchar(2)  YES
O`Reilly                          varchar(2)  YES

在使用另一个反引号转义列标识符中的任何反引号后,您需要将列标识符括在反引号中。

答案 1 :(得分:0)

正如其他人所说,请确保您的列名是安全的。

SQL注入可以从任何外部输入发生,而不仅仅是http请求输入。如果您使用从文件,Web服务或其他代码的函数参数,或其他代码的返回值,甚至从您自己的数据库中读取的内容,您可能会面临风险...... trust什么都没有!: - )

您可以确保列名称本身已转义。不幸的是,在大多数API或框架中没有内置函数可以做到这一点。因此,您必须使用正则表达式自己完成。

我还建议您了解PHP的内置数组函数(http://php.net/manual/en/ref.array.php)。您的许多代码可以更快地开发代码,并且它可能也会提高运行时性能。

以下是一个例子:

function quoteId($id) {
  return '`' . str_replace($id, '`', '``') . '`';
}

$q = $pdo->query("SHOW COLUMNS FROM my_table");
while ($field = $q->fetchColumn()) {
  $fields[] = $field;
}

$params = array_intersect_key($_POST, array_flip($fields));
$fieldList = implode(",", array_map("quoteId", array_keys($params)));
$placeholderList = implode(",", array_fill(1, count($params), "?"));
$sqlSub = "INSERT INTO other_table ($fieldList) VALUES ($placeholderList)";

$q = $pdo->prepare($sqlSub);
$q->execute($params);

在这个例子中,我将表中的列与post请求参数相交。这样我只使用也在列集中的那些post参数。它最终可能会在SQL中生成一个INSERT语句,其中包含的列数少于所有列,但如果缺少的列具有默认值或允许为NULL,则表示没问题。

答案 2 :(得分:-1)

有一种一种方法可以阻止SQL注入:确保查询字符串永远不会文本包含用户提供的内容内容,无论你如何尝试“消毒”它。

当您按照建议使用“占位符”时,SQL字符串的文本包含(可能是......)问号... VALUES (?, ?, ?)指示要插入参数的每个位置。每次执行查询时,都会提供相应的参数值列表

因此,即使为last_name提供的值为"tables; DROP TABLE STUDENTS;",SQL也永远不会将其视为“SQL字符串的一部分”。它只会将“最不寻常的last_name插入数据库。

如果您正在进行批量操作,那么您只需一次准备语句这一事实可以节省大量时间。然后,您可以根据需要多次执行该语句,每次都会向其传递一组不同的(或相同的)参数值。