0

我尝试使用参数将一些可选的 SQL 注入到准备好的语句中$and

public function loadInfoAndStatus($property_id, $property_item_type_id, $and, $returnArray = false)
{
    if (!isset($property_id) || empty($property_id)
     || !isset($property_item_type_id) || empty($property_item_type_id)
     || !isset($and) || empty($and)) {
        error_log(get_class() . " - " . __FUNCTION__ ." : required params not set or empty");
        return false;
    }

    $sql = "   SELECT pi.status, pi.info, pi.property_item_id "
           . " FROM ". self::TABLE ." pi "
           . " JOIN countries c ON c.country_id = pi.country_id "
           . " WHERE pi.property_id = ? "
           . "     AND property_item_type_id = ? "
           .       $this->con->real_escape_string($and)     // <--- here
           . " ORDER BY pi.status "
           . " DESC LIMIT 0,1";

    $err = "";
    if (!$stmt = $this->con->prepare($sql)) {
        $err .= "Prepare failed: (" . $this->con->errno . ") " . $this->con->error;
    }

    ...

但是,如果我调用该函数,例如

$row2 = Main::getModel("Property/Item")->loadInfoAndStatus(
    $id
    , $property_item_type_id
    , " AND c.iso = 'DE' "
    , true
);

提示: $and 可以是以下之一:

" AND c.iso <> 'DE' AND c.european <> 1 "
" AND c.iso <> 'DE' AND c.european = 1 "
" AND c.iso = 'DE' "

然后我得到“准备失败”,但没有错误消息。

生成的 SQL:

SELECT pi.status, pi.info, pi.property_item_id  FROM property_item pi  JOIN countries c ON c.country_id = pi.country_id  WHERE pi.property_id = ?      AND property_item_type_id = ?  AND c.iso = \'DE\'  ORDER BY pi.status  DESC LIMIT 0,1

如果我不使用它会起作用real_escape_string

我是否必须为每个新 sql 创建新函数,还是有其他方法?

4

2 回答 2

1

您必须在函数中列出所有可能的变体。

这是一项艰巨的任务,但您必须意识到这是唯一的方法。

public function loadInfoAndStatus($property_id, $property_item_type_id, $iso = null, $european = null, $returnArray = false)
{
    if (empty($property_id) || empty($property_item_type_id)) {
        error_log(get_class() . " - " . __FUNCTION__ ." : required params not set or empty");
        return false;
    }
    $parameters = [$property_id, $property_item_type_id];
    $sql = "   SELECT pi.status, pi.info, pi.property_item_id "
           . " FROM ". self::TABLE ." pi "
           . " JOIN countries c ON c.country_id = pi.country_id "
           . " WHERE pi.property_id = ? "
           . "     AND property_item_type_id = ? ";

    if ($iso) {
        $sql .= " AND c.iso <> ? ";
        $parameters[] = $iso;
    }
    if ($european === true) {
        $sql .= " AND c.european == 1 ";
    } elseif ($european === false) {
        $sql .= " AND c.european <> 1 ";
    }

    $sql .= " ORDER BY pi.status ";
    $sql .= " DESC LIMIT 0,1";

    $stmt = $this->con->prepare($sql);
    $stmt->bind_param(str_repeat("s", count($parameters)), ...$parameters);
    $stmt->execute();
    

我还从您的方法中删除了一些货物崇拜代码,以防您对原因感兴趣

于 2019-09-30T10:01:03.010 回答
1

我通过使用白名单方法解决了这个问题:

public function loadInfoAndStatus($property_id, $property_item_type_id, $and = "", $returnArray = false)
{
    if (empty($property_id) || empty($property_item_type_id) || empty($and)) {
        error_log(get_class() . " - " . __FUNCTION__ ." : required params not set or empty");
        return false;
    }

    if (!$this->isSqlInWhitelist($and, array(
        "AND c.iso = 'DE'"
        ,"AND c.iso <> 'DE' AND c.european = 1"
        ,"AND c.iso <> 'DE' AND c.european <> 1"
    ))) {
        error_log(get_class() . " - " . __FUNCTION__ ." : sql is not in whitelist.");
        return false;
    }

    $sql = "   SELECT pi.status, pi.info, pi.property_item_id "
           . " FROM ". self::TABLE ." pi "
           . " JOIN countries c ON c.country_id = pi.country_id "
           . " WHERE pi.property_id = ? "
           . "     AND property_item_type_id = ? "
           .       $and
           . " ORDER BY pi.status "
           . " DESC LIMIT 0,1";

    $stmt = $this->con->prepare($sql);
    ...

...

protected function isSqlInWhitelist($sql, $whitelist)
{
    if (!empty($sql)) {
        if (!in_array(trim($sql), $whitelist)) { return false; }
    }
    return true;
}
于 2019-09-30T10:48:26.713 回答