Php 这种动态SQL查询生成不会被注入吗?

Php 这种动态SQL查询生成不会被注入吗?,php,mysql,security,sql-injection,Php,Mysql,Security,Sql Injection,在我的脚本中是否有一些东西可能会逃过环境卫生的限制,或者它是否安全,不受大多数SQL注入的影响?按照我的理解,如果您将查询作为prepared参数传递,那么查询是如何生成的并不重要,对吗 Edit2:我编辑代码以反映绑定$\u POST值的建议 $q = $pdo->prepare('SHOW COLUMNS FROM my_table'); $q->execute(); $data = $q->fetchAll(PDO::FETCH_ASSOC); $key = array(

在我的脚本中是否有一些东西可能会逃过环境卫生的限制,或者它是否安全,不受大多数SQL注入的影响?按照我的理解,如果您将查询作为prepared参数传递,那么查询是如何生成的并不重要,对吗

Edit2:我编辑代码以反映绑定$\u 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);

不可以。所示的示例代码对于大多数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

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

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

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

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


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

如其他人所述,请确保列名是安全的

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

您可以确保列名本身已转义。不幸的是,在大多数API或框架中没有内置函数来实现这一点。所以你必须自己用正则表达式来做

我还建议您了解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,则无所谓。

如果将查询作为预处理参数传递,则无论查询是如何生成的。
错误。是的,这仍然是脆弱的。事实上,您就像没有准备查询一样容易受到攻击。使sql注入安全的方法不仅仅是准备查询,而是使用占位符准备查询并将值绑定到占位符。不!如果不使用绑定值,则不安全。不,如果要使其安全,则可以使用“描述”或“解释”数组键获取表中的列列表,如果它们是表单输入的名称,则可以更改。例如,可以使用列列表对它们进行交叉检查。然后对值使用准备好的语句,对键使用白名单。@aynber,但他们使用的是准备好的语句。它们只是使用连接,而不是绑定。如果不绑定,准备好的语句不会比标准sql更安全。@JonathanKuhn我用它修改了我的评论。因为对我来说,准备好的语句意味着绑定,但这只是我训练大脑的方式。列的名称如何不安全,因为它们是直接从不可变的表服务器端获取的,没有用户输入?此外,我还编辑了我的帖子,展示了一个列标识符如何不安全地包含在SQL文本中的示例,并且需要特殊处理才能使其安全。这就是为什么您会看到mysqldump和各种MySQL工具在backticks中包装标识符,即使backticks不是必需的。(总是这样做比检查是否需要更快。)现在我明白了。如果我还允许用户创建他们自己的表模式,然后允许他们运行这个脚本,他们就可以完全按照您通过列名所说的做。这很有意义,很好。列名不能作为绑定参数传递。要处理这些问题,你需要适当地避开它们,比如<代码>'`'。str_replace($colname,`,`,``)`)。我认为代码的其余部分遵循最佳实践,使用静态的
绑定占位符作为值,并将实际值作为绑定参数提供,而不是作为SQL文本的一部分。当然,列名应该始终是固定的。绝不允许用户输入直接决定要操作哪些表和/或列。不要让用户“创建自己的表”
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);