准备好的陈述是否必要?

时间:2014-07-09 19:23:48

标签: php mysql mysqli prepared-statement sql-injection

我总是检查/限制/清理我在数据库查询中使用的用户变量

像这样:

$pageid = preg_replace('/[^a-z0-9_]+/i', '', $urlpagequery); // urlpagequery comes from a GET var

$sql = 'SELECT something FROM sometable WHERE pageid = "'.$pageid.'" LIMIT 1';

$stmt = $conn->query($sql);

if ($stmt && $stmt->num_rows > 0) {

    $row = $stmt->fetch_assoc();

    // do something with the database content

}

我不知道如何使用准备好的语句或进一步转义可以改善那种情况下的任何事情?注射似乎不可能在这里,不是吗?

我已经尝试搞乱准备好的语句了......我有点看到了这一点,即使只需要半简单的查询就需要花费更多的时间和思考(sssiissisis等)。

但是因为我总是在数据库交互之前清理用户输入,所以似乎没必要

你可以开导我吗?

3 个答案:

答案 0 :(得分:4)

问题是你如何定义" 改进"在这种背景下。在这种情况下,我会说它对代码的功能没有任何影响。

那么你有什么不同?你说这对你来说更容易,更快。情况可能如此,但只是培训问题。一旦你习惯了准备好的陈述,你就会一样快地写出来。

与其他程序员的区别?在您分享此代码的那一刻,对方很难完全理解,因为准备好的陈述是一种标准(或者在一个完美的世界中)。因此,通过使用其他东西,它实际上更难以理解其他人。


更多地谈论这一小段代码毫无意义,因为实际上它并不重要,它只是一个非常简单的陈述。但是想象一下你写了一个更大的脚本,以后会更容易阅读和修改吗?

$id = //validate int
$name = //validate string
$sometext = //validate string with special rules

$sql = 'SELECT .. FROM foo WHERE foo.id = '.$id.' AND name="'.$name.'" AND sometext LIKE "%'.$sometext.'%"';

你总是需要问问自己:我是否正确验证了我使用的所有变量?我犯了错误吗?

而当你使用这样的代码时

$sql = $db->prepare('SELECT .. FROM foo WHERE foo.id = :id AND name=":name" AND sometext LIKE "%:sometext%"');
$sql->bind(array(
    ':id' => $id,
    ':name' => $name,
    ':sometext' => $sometext,
));

如果你做的一切都是正确的,不用担心,因为PHP会为你解决这个问题。

当然,这也不是一个复杂的查询,但有多个变量应该证明我的观点。


所以我的最终答案是:如果你是一个完美的程序员,永远不会忘记或犯错并独自工作,那就按你的意愿去做。但如果你不这样做,我会建议使用标准,因为它们存在是有原因的。 并不是说您无法正确验证所有变量,而是您不应该这样做。

答案 1 :(得分:4)

你最好一直使用预备语句。

正则表达式只是部分解决方案,但不方便或多功能。如果您的变量不适合可以使用正则表达式过滤的模式,那么您就无法使用它们。

所有" ssisiisisis"东西是Mysqli的神器,恕我直言,这是不必要的混淆。

我改用PDO:

$sql = 'SELECT something FROM sometable WHERE pageid = ? LIMIT 1';
$stmt = $conn->prepare($sql);
$stmt->execute(array($pageid));

请参阅?无需正则表达式过滤。无需在连接的部分之间使用.引用或分解字符串。

在PDO中传递一组变量很容易,那么你就不必做冗长的变量绑定代码。

PDO还支持命名参数,如果你有一个关联的值数组,这可以很方便:

$params = array("pageid"=>123, "user"=>"Bill");
$sql = 'SELECT something FROM sometable WHERE pageid = :pageid AND user = :user LIMIT 1';
$stmt = $conn->prepare($sql);
$stmt->execute($params);

如果启用PDO例外,则无需测试查询是否成功。您将知道它是否因为抛出异常而失败(FWIW,您也可以在Mysqli中启用异常)。

您不需要测试num_rows(),只需将提取放入while循环中即可。如果没有要获取的行,则循环立即停止。如果只有一行,那么它会循环一次迭代。

while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {

    // do something with the database content

}

准备好的语句比过滤和字符串连接更容易,更灵活,在某些情况下它们是faster than plain query() calls

答案 2 :(得分:3)

准备好的陈述有时会更快。但是从你问这个问题的方式我会认为你不需要它们。

  

那么使用预准备语句可以获得多少额外性能?结果可能有所不同在某些情况下,当需要从localhost检索大量数据时,我已经看到了5x +性能改进 - 在这种情况下,数据转换实际上可能占用大部分时间。它还可能在某些情况下降低性能,因为如果只执行一次查询,则需要额外往返服务器,或者因为查询缓存不起作用。

http://www.mysqlperformanceblog.com/

为您带来更快的速度
  

我没有看到使用预处理语句或进一步转义如何改善该场景中的任何内容?

你没错。

P.S。我投了你的问题,因为在你问之前似乎没有做过什么研究。