返回 登录
0

编写SQL时PHP开发人员所犯的5个常见错误

原文:5 Common Mistakes PHP Developers Make when Writing SQL
作者:EverSQL Team
翻译:Diwei

译者注:作者在本文中介绍了由于在用法不了解或者经验不丰富的情况下,有些程序员可能会犯一些错误,导致项目被SQL注入,作者从5个方面介绍了应该如何避免这些情况。以下为译文。

杜绝使用MySQL API的旧版本

使用PHP开发时有好几种方法可以连接MySQL数据库。最常见的是MySQL API、MySQLi API和PDO API(PHP数据对象)。与MySQL API旧版本相比,后面两种方法支持更多的功能,并且更加安全。如果你使用的是旧的“mysql_”函数,那么你应该马上放下手上的工作,然后开始学习新的PDO API。这些旧的mysql函数被弃用,并且PHP 7.x以后的版本也不会再支持了。

糟糕的实践:

<?php  
$con = mysql_connect("localhost", "root", "mypass") or 
    die("Could not connect: " . mysql_error());  
mysql_select_db("tutorials");  
$result = mysql_query("select * from tutorials");  
echo "<h2>Here is a list of the topics:</h2>";  
while ($row = mysql_fetch_array($result)) {  
    echo $row['name']."<br />";  
}  
mysql_close($con);  
?>  

优秀的实践:

require_once('includes/conn.inc.php'); 
$sql= "SELECT name, age FROM employees WHERE company_id = 10"; 
$stmt = $pdo->query($sql); 
$row = $stmt->fetch(PDO::FETCH_ASSOC);
echo $row['name'];
echo $row[age];

不要使用转义函数处理客户端输入的内容

使用mysql_real_escape_string对查询变量进行手工转义是不安全的,原因有两个:

  1. 如果经常使用这个方法,你难免会弄错一次。只要有一个漏洞,黑客就可以将代码注入到SQL查询中。

  2. 在使用字符串变量时,你应该记住使用引号,很多人都没有这个意识。

mysql_real_escape_string的替换者就是prepared statements(参见下面的更多信息)。

不要假设预处理在PHP中总是安全的

Prepared statements预处理的目标是将查询与数据分离,以便将数据正确地插入到查询的参数中,而不需要任何操作选项。在大多数情况下,预处理的语句应该是非常安全的,它被认为是为查询注入用户输入参数的最佳实践技巧。

代码样例:

require_once('includes/conn.inc.php'); 
$sql= "SELECT * FROM employees";
$stmt = $pdo->prepare($sql);
$stmt->execute();
$result = $stmt->fetchAll();
foreach($result as $row){
 echo "
<li>{$row['employeeName']}</li>";
}

那么,预处理在PHP中有什么不足呢?在将用户输入的参数值注入到查询语句这种情况下,就会有问题,因为预处理不支持这种注入。例如,MySQL PDO不支持在LIMIT情况中注入参数(使用占位符“?”)。此外,用户输入的内容不能在查询中插入表名或列名。如果你在这些情况下使用预处理,你应该手动地对数据进行人工处理,或者比这更好的方式就是找一个已经测试过的库,自动替你去做这些事。

不要假设ORM框架不会受到SQL注入攻击

使用ORM框架是非常棒的(尽管我敢肯定)。虽然这么说,这并不意味着它不会出现SQL注入攻击的情况。的确,在一个ORM生成的查询中想注入代码是很困难的,但是如果程序员不小心犯了个错误,这将会打开一个漏洞。在大多数情况下,在不使用预处理的情况下,将用户输入的值连接到ORM查询时,被注入的SQL语句就会执行。是的,像Doctrine这样的ORM框架提供了使用预处理的功能,所以使用它。从不将字符串连接到查询,无论是ORM查询还是原始SQL查询。

当然,遵守这些规则是不够的——为了避免给任何SQL注入留有一丝喘息的机会,确保你在编码之后一定要测试ORM用法和实现。

糟糕的实践:

在下面的代码示例中,你可以看到从用户输入的一个参数被连接到Doctrine DQL,它将应用程序暴露给SQL注入。

<?php

// INSECURE
$dql = "SELECT u
          FROM MyProject\Entity\User u
         WHERE u.status = '" .  $_GET['status'] . "'
     ORDER BY " . $_GET['orderField'] . " ASC";

糟糕的实践:

正如推荐给非ORM用户一样,在使用ORM框架(如Doctrine)时,推荐使用预处理语句。

<?php

$orderFieldWhitelist = array('email', 'username');
$orderField = "email";

if (in_array($_GET['orderField'], $orderFieldWhitelist)) {
    $orderField = $_GET['orderField'];
}

$dql = "SELECT u
          FROM MyProject\Entity\User u
         WHERE u.status = ?1
     ORDER BY u." . $orderField . " ASC";

$query = $entityManager->createQuery($dql);
$query->setParameter(1, $_GET['status']);

样例的源码地址:Doctrine文件

不要低估字符编码的力量

根据OWASP(开放式Web应用程序安全项目),不使用utf8mb4会导致你的应用程序暴露给各种类型的攻击(你可以阅读更多关于encoding bypassing的信息)。另外,你应该经常在PHP和MySQL中使用utf8mb4,以获得更好和更标准的多语言支持。

代码样例:

$dsn = 'mysql:host=example.com;dbname=testdb;port=3306;charset=utf8mb4';

总结

我们研究了许多PHP开发人员所犯的5个常见错误。其中一些是由于缺乏知识而发生的(新框架总是可用的,确保你知道它们)。有些是因为缺乏经验(阅读、阅读和阅读更多,以确保使用最安全和最好的API)。祝你下一个项目好运!

评论