(double)$ user_input和bind_param('d',$ user_input)之间的安全性差异

时间:2013-05-31 03:44:42

标签: php mysqli

假设我要执行这样的预备声明:

$qry->prepare('UPDATE table_name SET column1 = ? string_column = ? WHERE column3 = ? AND column4 = ?');
$qry->bind_param('sbid', $string, $blob, $int, $double);

$int = 'non int value'; /* gives 0 in the database */
$blob = 'some string';
$string = 'another string';
$double = $double;

$qry->execute();
$qry->close();

我们只想说我只想执行一次查询,我只是在安全名称中使用了预处理语句。从我一直在阅读的内容来看,只使用一次准备好的查询会产生更多的开销,这相当于降低了安全性方面的性能。话虽这么说 - 像这样一次做同样的查询会有什么性能/安全性差异。

$int = (int) $int;
$blob = "'" .mysql_real_escape_string($blob) ."'";
$string = "'" .mysql_real_escape_string($blob) ."'";    
$double = (double) $double;

$db->query("UPDATE SET column1 = $int, column2 = $blob WHERE column3 = $string AND column4 = $double ");

PS。我对Prepared语句如何改进性能以及单个查询的安全性和速度差异感兴趣。

3 个答案:

答案 0 :(得分:3)

这有很多。一些随机点

  • 如果MySQL服务器存在大量连接,则单次使用预准备语句执行会产生(超过理论上)性能损失,这会更高。 (想一想:上下文切换)
  • 但是你不应该运行如此接近其限制的数据库服务器,这会产生差异。
  • 但是你并不总是有选择(想想:共享主机)

或:

  • 有一些(甚至很多)案例,其中准备好的语句提供安全性好处 - 有很多业务逻辑,其中不涉及用户生成的数据(想想:Jointables) ,只携带ID)或用户生成的数据必须事先通过其他原因进行验证(思考:价格计算,memcached查找,......)
  • 为每个查询选择多种样式之一会导致无法维护的代码。
  • 它有时是不可避免的(想一想:IN ( )构造没有准备好的查询支持)

经常被忽视:

  • 准备好的查询有时会使与RDBMS无关的内容变得更加困难
  • 但是准备好的查询提供了针对SQL注入的最佳知识保护。

我的最爱:

  • 通常建议始终使用准备好的查询
  • 但是这个星球上的大多数生物会建议你吃粪便或腐烂的有机物质。

因此,风格的选择通常必须根据具体情况进行。我们采用了在标准化库中封装所有数据库访问包括参数管理的方式,简单地require()编辑,因此您可以直接替换准备好的查询,转义或其他任何内容想要和你的RDBMS支持。

答案 1 :(得分:3)

谢谢你提出这个好问题。

事实上,您可以一次使用两种方法。

大多数人确实将准备好的声明的想法与主要DBMS提供的[非常有限的]实现相混淆。虽然后者可以被质疑,但前者确实是唯一方式。

看看这个例子。让我们使用safeMysql运行您的查询:

$sql = "UPDATE SET column1 = ?i, column2 = ?s WHERE column3 = ?s AND column4 = ?s";
$db->query($sql, $string, $blob, $int, $double);

它执行代码所做的字符串格式化,但内部。为什么?因为它在内部实现的方式无关紧要(通过本机预处理语句或手动格式化),但必须使用预准备语句以任意方式汇编查询。

关于准备好的陈述有一些基本要点,被大多数人忽略了:

  1. 它使格式始终完整(尽管在您的示例中,您正在做正确的事情并使完整的格式化,但它仍然非常很容易陷入不完整的一个,就像这样:

    $colname = "`$colname`";
    
  2. 您的格式始终是正确的格式。它不会让你做像

    这样的事情
    $colname = "`" .mysql_real_escape_string($colname) ."`";
    

    这将是无用的并引导你注射

  3. 它会格式化强制性。通过以当前方式组装查询,很容易忽略一两个变量。

  4. 它将在尽可能接近查询执行时进行正确的格式化。这是非常重要的一点,因为
    • 它不会破坏您的源变量(如果查询失败并且您想要回显它会怎么样?)
    • 它不会让您将格式代码移到远离查询的地方,这可能会导致致命的后果。
    • 毕竟,它会使你的代码显着缩短,而不需要那么无聊的手动格式化!
  5. 这是准备好的陈述的真正好处,它保证了安全性,从而使它们过于流行。虽然服务器端准备工作虽然很聪明,但它只是一个特定的实现。

    此外,以准备好的声明为指导,可以为可能添加到查询中的所有创建一个占位符(例如标识符或数组),使其成为<强>真实安全方便使用。

    记住所有事情必须在他们的数据库访问库中实现准备好的语句的想法,以使代码安全和简短。

    来自safeMysql的几个例子:

    $name = $db->getOne('SELECT name FROM table WHERE id = ?i',$_GET['id']);
    $data = $db->getInd('id','SELECT * FROM ?n WHERE id IN ?a','table', array(1,2));
    $data = $db->getAll("SELECT * FROM ?n WHERE mod=?s LIMIT ?i",$table,$mod,$limit);
    
    $ids  = $db->getCol("SELECT id FROM tags WHERE tagname = ?s",$tag);
    $data = $db->getAll("SELECT * FROM table WHERE category IN (?a)",$ids);
    
    $data = array('offers_in' => $in, 'offers_out' => $out);
    $sql  = "INSERT INTO stats SET pid=?i,dt=CURDATE(),?u ON DUPLICATE KEY UPDATE ?u";
    $db->query($sql,$pid,$data,$data);
    

    尝试使用传统的mysql(i),然后查看它带来的代码量。

    你可能会注意到,使用可用的预处理语句,你必须用类型标记它们,因为有更多的类型而不仅仅是简单的字符串,这是告诉驱动程序如何格式化你的唯一可靠方式变量

答案 2 :(得分:1)

我认为从安全角度来看它们同样安全,但使用prepare不仅可以使您的SQL安全,还可以使您感到安全。您不能相信自己手动转义并始终转换为正确的类型。如果您编写10,000个不同的SQL查询,您将倾向于忘记逃避一两个。

总而言之,prepare是一种更好的反对SQL注入的习惯。将PHP变量直接放到SQL查询中会让我在晚上睡觉时感到不安。