如何使用php和mysql阻止sql注入

时间:2010-06-15 17:09:32

标签: php mysql sql-injection

我有一个访问者可以输入数据的表单,我想通过$ _POST变量将这些数据存储在mysql数据库中。我需要什么来阻止sql注入?

4 个答案:

答案 0 :(得分:6)

使用 prepared statements

答案 1 :(得分:3)

我在2010年5月的PHP TEK-X会议上发表了关于这个主题的演讲,我试图介绍防御SQL注入的多种方法。没有一种方法在所有情况下都是最好的,因此您应该学习多种方法并使用所有方法:

  • 在将其插入SQL查询之前,验证用户输入或来自外部源的任何其他内容(甚至是您自己数据库中的数据)。例如,您可以使用PHP的filter扩展名或正则表达式。

  • 强制外部内容格式正确。例如,(int) $_POST["userid"]类型转换为内容为普通整数,因此可以安全使用。

  • 在SQL表达式中包含动态内容代替文字值时,请使用带参数的预准备查询。请注意,PHP中的普通mysql扩展名不支持查询参数 - 请使用PDO。我不使用mysqli,因为它的API不一致且难以使用。

  • 使用IN()谓词时,不能将一个参数用于值列表。连接多个参数占位符,就像列表中的值一样多。这并不难,只需要一两行代码:

    $sql = "SELECT ... FROM ... WHERE user_id IN ("
        . join(",", array_fill(0,count($userid_list),"?")) . ")";
    $pdoStmt = $pdo->prepare($sql);
    $pdoStmt->execute($userid_list);
    
  • 使用动态表名,列名或SQL关键字时,不能使用查询参数。您必须插入动态内容。但您可以使用白名单技术将不受信任的内容映射到合法,安全的标识符和关键字。

有关更多信息和示例,请参阅我的演示文稿SQL Injection Myths and Fallacies

您也可能喜欢我的新书SQL Antipatterns: Avoiding the Pitfalls of Database Programming。我的书中有一章关于SQL注入。

答案 2 :(得分:0)

答案 3 :(得分:-1)

您必须遵循一些规则,同时向查询添加任何数据,无论来自何处 - 来自用户或表单或任何内容。规则始终保持不变。

要向数据库发送查询,您有两个选项:

  1. 以常规方式构建查询,使其看起来与您可以在sql控制台中运行的SQL查询完全相同 要做到这一点,人们应该理解一整套规则,而不仅仅是“使用mysql_real_escape_string”。
    规则如:

    • 字符串应该用引号括起来并进行转义。这是逃避的唯一意义:它只是简单的分隔符! (和一些其他字符 - 字符串终止字符和转义字符本身)。如果没有周围的引号,mysql_real_escape_string就没用了。
    • 数字应该明确地转换为它的类型。虽然数据编号可以像字符串一样受到威胁,但有一些数字,如LIMIT子句参数,不能转义,只能转换。
  2. 分别发送查询和数据 这是最优选的方式,因为它可以简化为“使用绑定”。所有字符串,数字和LIMIT参数都可以绑定 - 完全不用担心 使用此方法,您的查询将占位符按原样发送到数据库,绑定数据以单独的数据包发送,因此,它不会干扰。 它就像代码数据分离。您发送与数据分开的程序(查询本身)。

  3. 上述所有内容仅涵盖数据插入。 但有时我们必须使查询更加动态,添加运算符或标识符 在这种情况下,每个动态参数都应该在我们的脚本中进行硬编码,并从该集合中选择 例如,要进行动态排序:

    $orders  = array("name","price","qty");
    $key     = array_search($_GET['sort'],$orders));
    $orderby = $orders[$key];
    $query   = "SELECT * FROM `table` ORDER BY $orderby";
    

    或动态搜索:

    $w     = array();
    $where = '';
    
    if (!empty($_GET['rooms']))     $w[]="rooms='".mesc($_GET['rooms'])."'";
    if (!empty($_GET['space']))     $w[]="space='".mesc($_GET['space'])."'";
    if (!empty($_GET['max_price'])) $w[]="price < '".mesc($_GET['max_price'])."'";
    
    if (count($w)) $where="WHERE ".implode(' AND ',$w);
    $query="select * from table $where";
    

    在此示例中,我们仅向查询添加用户输入的数据,而不是字段名称,这些都是脚本中的硬编码。 对于绑定,算法非常相似

    等等。