Trouble addressing sql injection with PDO

tl;dr: Rewrote database class using mysql to use PDO instead to make use of prepared statements, but running sqlmap from Kali still extracts tables from the website's database. How?

Longer version:

It came to my attention that one of our websites at work has a SQL injection vulnerability. So I got to looking into it, ran sqlmap from Kali, and it proceeded to extract all the tables from that website's database. Looking into it, the website was using the old mysql api, and after some research I decided switching to PDO to make use of prepared statements would be the answer to fix this vulnerability. So I've mostly rewritten our database class and calls to it to use PDO.

Last night I tested again using sqlmap, and it proceeded to extract all the tables from that website's database. I was extremely perplexed, and the only thing I could think of is maybe it's using emulated prepared statements instead of native prepared statements, so I added the option upon creation of the PDO handle to disable emulation. But apparently that didn't make any difference, either.

The Question:

What am I missing? How is there still a sql injection vulnerability that sqlmap is exploiting? How do I effectively address this?

Code:

Here is the calling code:

public function Load($DbObject, $userName, $password) {
    /* @var $DbObject DB */
    if ($userName == "" || $password == "") {
        $this->errMessage = "You must complete all fields.";
        return (FALSE);
    }
    $fields = array("generic", "db", "fields");
    $where_fields = array("userName", "password");
    $where_values = array($userName, $password);
    if ($DbObject->SelectQuery("db_table", $fields, $where_fields, $where_values)) {
        if ($DbObject->RecordCount() == 1) {
            $ret_val = TRUE;
            $results = $DbObject->FetchResult();
            for ($i = 0; $i < sizeof($fields); $i++) {
                $this->{"$fields[$i]"} = $results[0][$i];
            }
        } else {
            $this->errMessage = "<error_message>";
            $ret_val = FALSE;
        }
    } else {
        $this->errMessage = "<another_error_message>";
        $ret_val = FALSE;
    }
    return ($ret_val);
}

Here's the relevant code from the database class:

function SelectQuery($db_table, $db_fields, $where_fields = NULL, $where_values = NULL) {
    $ret_val = TRUE;
    $query = "SELECT " . implode(", ", $db_fields) . " FROM " . $db_table;

    $numFields = count($where_fields);

    if ($numFields > 0) {
        $query .= " WHERE ";
        $i = 1;
        foreach ($where_fields as $field) {
            $query .= "$field = ?";
            if ($i < $numFields) {
                $query .= " AND ";
            }
            $i++;
        }
    }

    try {
        $stmt = $this->db_handle->prepare($query);
        if (is_null($where_fields)) {
            $stmt->execute();
        } else {
            $stmt->execute($where_values);
        }
        $this->db_result = $stmt->fetchAll();
        $this->db_numRecords = count($this->db_result);
    } catch (PDOException $e) {
        $ret_val = FALSE;
        $this->db_errNumber = '<err_msg>' . $stmt->errorCode();
        $this->db_errMessage = '<second_err_msg>' . $stmt->errorInfo();
        $this->db_errDetailMessage = '<third_err_msg>' . $query;
    }
    return $ret_val;
}

function DB($type, $user, $pass, $host, $name) {
    // set the default values for the DataBase class
    $this->db_type = $type;
    $this->db_user = $user;
    $this->db_password = $pass;
    $this->db_host = $host;
    $this->db_name = $name;
    $this->db_dsn = $type . ":host=" . $host . ";dbname=" . $name;
}

function OpenConnection() {
    $returnValue = TRUE;
    try {
        $this->db_handle = new PDO($this->db_dsn, $this->db_user, $this->db_password, array(PDO::ATTR_EMULATE_PREPARES => FALSE));
        $this->db_handle->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
    } catch (PDOException $e) {
        $returnValue = FALSE;
        $this->db_errNumber = 002; /* ??? */
        $this->db_errMessage = "<error_message>" . $e->getMessage();
    }
    return ($returnValue);
}

And here's the sql statement it is generating when a user logs in:

SELECT generic, db, fields FROM db_table WHERE userName = ? AND password = ?

According to sqlmap, here's the payload it's using: (redacted)

Any guidance or advice you could provide on making this code more robust against SQL injection vulnerabilities would be very much appreciated!!

EDIT - Here is some additional information about my setup that may or may not be relevant: We're using PHP 5.4.16 and MariaDB 5.5.40. From phpinfo() it appears the pdo_mysql driver we are using is actually supplied by MariaDB rather than using mysqlnd. This seems appropriate since we're using MariaDB instead of MySQL. Could this at all affect using native prepared statements?

链接地址: http://www.djcxy.com/p/16760.html

上一篇: Idiorm pdo准备了声明

下一篇: 用PDO解决SQL注入问题