使用PDO但仍可以SQL注入

时间:2016-07-06 07:02:02

标签: php mysql pdo

直到最近,我的PHP应用程序中的所有数据库查询都通过一个函数将所有内容放入一个不安全的“mysqli_query”中。出于显而易见的原因,我想摆脱这种情况,转而使用PDO来防止注射。

一位乐于助人的用户提供了这个PDO类/功能作为替代方案:

class   DatabaseConfig
    {
        private static  $singleton;

        public  function __construct()
            {
                if(empty(self::$singleton))
                    self::$singleton    =   $this->connect();

                return self::$singleton;
            }

        public  function connect($host = "localhost", $username = "username", $password = "password", $database = "database")
            {
                // Create connection
                $opts   =   array(  PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
                                    PDO::ATTR_EMULATE_PREPARES => false,
                                    PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC);
                $conn   =   new PDO('mysql:host='.$host.';dbname='.$database, $username, $password,$opts);

                return $conn;
            }
    }
// This is a query class that will run your sqls
class   QueryEngine
    {
        private $results;

        private static  $singleton;

        public  function __construct()
            {
                if(empty(self::$singleton))
                    self::$singleton    =   $this;

                return self::$singleton;
            }

        public  function query($sql = false,$bind = false)
            {
                $this->results  =   0;

                $db     =   new DatabaseConfig();
                try {
                        if(!empty($bind)) {
                                $query  =   $db ->connect()
                                                ->prepare($sql);
                                $query->execute($bind);
                            }
                        else {
                                $query  =   $db ->connect()
                                                ->query($sql);
                            }

                        $this->results  =   $query;
                    }
                catch (PDOException $e)
                    {
                        die($e->getMessage());
                    }

                return $this;
            }

        public  function fetch()
            {
                while($row = $this->results->fetch())
                    $result[]   =   $row;

                return (!empty($result))? $result : 0;
            }
    }


function dbcommand($req,$bind = false)
    {   
        // Create query instance
        $qEngine    =   new QueryEngine();
        // Run the query
        $qEngine->query($req,$bind);
        // If select, fetch array
        if(strpos(strtoupper($req), 'SELECT') === 0)
            return $qEngine->fetch();
        // The query already ran, so you can return whatever you want
        // For ease I am just returning true
        elseif(strpos($req, 'INSERT INTO') === 0)
            return  true;
    }

上面的代码工作得很好,但问题是我仍然可以在使用dbcommand之前将SQL注入到我没有过滤的语句中。我的印象是PDO会自动解决注射问题,但我显然是错误地采用了它。谁能开导我?

1 个答案:

答案 0 :(得分:3)

此“dbcommand”函数旨在以这种方式使用:

$row = dbcommand("SELECT * FROM users WHERE id=?", [$id]);

在这里,你必须用问号替换查询中的所有变量,同时以数组的形式将变量作为第二个参数发送。

或者您可以使用命名占位符和关联数组:

$update = [
  'name' => $_POST['name'],
  'id'   => $_POST['id'],
]
dbcommand("UPDATE users SET name=:name WHERE id=:id", $update);

只有当变量被占位符替换时,PDO才会保护您的查询。

另一方面,虽然这个功能本身就是一个好的意图,但它严重降低了香草PDO的可用性。例如,对于大型数据集,建议不要一次获取所有数据,但此功能不提供此选项。或者此功能不会让您获得受影响的行数。要使一个简单的PDO包装器可用,应该让它返回一个PDOStatement实例。这是必不可少的,因为在这个课程中有太多的功能被简单地解雇了。

对于允许您使用PDO的所有强大功能的课程,请参阅我的Simple yet efficient PDO wrapper,尤其是“示例”部分。除了设置数据库凭据外,所有示例都可以在没有任何先前设置的情况下运行。

只是为了澄清:您的班级不支持的链接中可能的用例列表:

  • 预备语句多次执行
  • 获取插入ID
  • 逐个循环获取行
  • 获得一行
  • 获取单字段值
  • PDO支持的所有数十种预定义格式获取行数
  • 获取受影响的行数