首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >使用PDO实现安全可靠的面向对象插入

使用PDO实现安全可靠的面向对象插入
EN

Stack Overflow用户
提问于 2013-07-16 19:19:03
回答 2查看 979关注 0票数 2

使用PDO实现安全可靠的面向对象插入

这段代码对SQL注入安全吗?它使用预准备和参数化语句。如果不是,我应该怎么做,因为我只想通过面向对象的过程来使用它,在这个过程中我可以插入列名和列值。

代码语言:javascript
复制
    <?php

        class CommunItY
        {
            const community_host = "localhost";
            const community_db = "DB";
            const db_username = "root";
            const db_password = "";
            private $conn = null;
            public $trace = "";

            public function insert($table ,$values = array())
            {
            try{
                foreach ($values as $field => $v)
                {
                    $ins[] = ':' . $field;
                }
                $ins = implode(',', $ins);
                $fields = implode(',', array_keys($values));
                $sql = "INSERT INTO $table ($fields) VALUES ($ins)";  
                $ready = $this->conn->prepare($sql);
                foreach ($values as $f => $v)
                {
                    $ready->bindValue(':' . $f, $v);
                }
                $ready->execute();
            }
            catch(Exception $e){
            $this->trace .= " • insertion error • ". $e->getMessage();
            }
            }//end of method

        public function __construct(){
        $connectionString = sprintf("mysql:host=%s; dbname=%s; charset=utf8", 
                                CommunItY::community_host, CommunItY::community_db);
            try {
                $this->conn = new PDO($connectionString, CommunItY::db_username, CommunItY::db_password);
                $this->conn->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);    
                $this->conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
            } //end of connection by PDO
            catch(PDOException $e){
            $this->trace .= " • ". $e->getMessage();
            }
        }//end of construct

        public function __destruct(){
        $this->conn = null; //close connection
        } //end of destruct

    }

calling...

    $call = new Contact()
    $call->insert(table_x, array('col1' => 'value1', 'col2' => 'value2'));
EN

回答 2

Stack Overflow用户

发布于 2013-07-28 15:21:29

可能不会。您已经在字段名称中引入了注入。如果您可以保证字段名称总是由您的代码生成,而不是从外部来源生成,这可能是可以的。但这是一个公共方法。因此,系统中的任何一点代码都可能试图做一些“聪明”的事情,并最终通过向insert方法传递一个参数来打开一个漏洞。

您应该将传入的字段名放在引号中进行转义(或者,如果您使用的是MySQL,并且没有启用ANSI兼容性,请使用反引号)。您还必须对名称中的所有引号进行转义。

代码语言:javascript
复制
$fields = implode(',', array_map(function($name) {
    return '"' . str_replace('"', '""', $name) . '"' ;
}, array_keys($values)));

对于这些值,您应该只使用位置参数(?)或者自己起名字。我不认为PDO有转义:bind_param名称的机制。

还要注意的是,由于PHP对7位ASCII范围之外的字符的糟糕处理,如果有人开始使用字符串的内部字节编码,这可能仍然不是100%安全的。在这种情况下,唯一安全的方法是首先确保字段名只包含预期的字符,或者根据众所周知的字段名列表对其进行验证(可能是通过使用INFORMATION_SCHEMA检查表列)。

票数 1
EN

Stack Overflow用户

发布于 2013-07-28 17:01:08

您的代码可能不会受到SQL注入的影响。但也有一个缺陷。您不应允许使用任何类型的表名或字段名。更好的解决方案是为您想要表示的每个对象创建一个类。假设你有苹果和香蕉作为你的应用程序的重要部分。然后创建类Apple和类Banana,这两个类分别表示数据库中的一个香蕉或苹果条目。

对于数据库的实际编辑,您可以分别创建一个能够更新苹果或香蕉的类AppleEditor或BananaEditor。这样,您就有了一组固定的字段,并且只有值是可变的。

回到您的代码:我不会使用const作为数据库凭据。因此,任何脚本都可以访问它们。最好在私有静态变量中使用它们。

然后是类名的问题。在call部分下面,您有一个类Contact,但在顶部,您的类被命名为CommunItY。为了有一个最小的代码设置,我要重新注释以下内容:

代码语言:javascript
复制
class Contact
{
    private static $community_host = "localhost";
    private static $community_db = "DB";
    private static $db_username = "root";
    private static $db_password = "";

    /**
     * the database connection
     * @var \PDO
     */
    private $conn = null;
    private $trace = "";

    /**
     * name of the contact
     * @var string
     */
    private $name = '';

    /**
     * address of the contact
     * @var string
     */
    private $address = '';

    private $contactID = 0;

    public function __construct($id) {
        $this->connectToDatabase();
        $this->contactID = intval($id);
    }//end of construct

    /**
     * Updates a contact.
     * 
     * @param string $name
     * @param string $address
     */
    public function update($name, $address)
    {
        $name = trim($name);
        $address = trim($address);
        try{
            $sql = "UPDATE contact SET name = ?, address = ? WHERE contactID = ?";
            $ready = $this->conn->prepare($sql);
            $ready->bindValues(1, $name, \PDO::PARAM_STR);
            $ready->bindValue(2, $address, \PDO::PARAM_STR);
            $ready->bindValue(3, $this->contactID, \PDO::PARAM_INT);
            $ready->execute();
        }
        catch(\PDOException $e){
            $this->trace .= " • insertion error • ". $e->getMessage();
        }
    }//end of method



    public function __destruct(){
        $this->conn = null; //close connection
    } //end of destruct


    private function connectToDatabase() {
        $connectionString = sprintf("mysql:host=%s; dbname=%s; charset=utf8",
            self::$community_host, self::$community_db);
        try {
            $this->conn = new \PDO($connectionString, self::$db_username, self::$db_password);
            $this->conn->setAttribute(\PDO::ATTR_EMULATE_PREPARES, false);
            $this->conn->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
        } //end of connection by PDO
        catch(\PDOException $e){
            $this->trace .= " • ". $e->getMessage();
        }
    }
}

$call = new Contact(1);
$call->update('name', 'address');

这当然不是一个完美的情况。但它显示了如何处理它。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/17675145

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档