как получить результаты из отчета о подготовке PDO - PullRequest
0 голосов
/ 29 сентября 2018

Я использую оператор подготовки PDO для выбора результата.

Мой index.php:

include('operations.php');
$userprofileobj = new operations();
if(isset($_SESSION['user_email']))
{
$results = $userprofileobj->verify_user('account',$_SESSION['user_email'])->fetch(PDO::FETCH_ASSOC);

echo $results['username'];
}

Мои операции.php:

<?php
include('userclass.php');
class operations extends userclass{
    public function verify_user($table_name,$user_email)
{
    $stmt = $this->con->prepare("select * from ".$table_name." where username = :user_email");

    $stmt->execute([
        ':user_email' => $user_email,
    ]);

return $stmt->fetchAll(PDO::FETCH_ASSOC);

}
}

Я пытаюсь сопоставить электронную почту, и результат должен быть получен в index.php.Но я получил ошибку Неустранимая ошибка: вызов функции-члена fetchColumn () для логического значения в operations.php в строке 84

Ответы [ 2 ]

0 голосов
/ 29 сентября 2018

Вы result переменная (помимо того, что перезаписана) не то, что вы думаете.Это PDOStatment

Попробуйте вместо этого

$stmt = $this->con->prepare("select * from ".$table_name." where username = :user_email");

$stmt->execute([
    ':user_email' => $user_email,
]);

if (false !== ($row = $stmt->fetchColumn()))
{
    return $row;
}

Однако это вернет только первый столбец первой строки.Вместо этого вы, вероятно, захотите:

 return $stmt->fetchAll(PDO::FETCH_ASSOC);

Я изменил $result на $stmt, потому что это не результат, это объект заявления.

Исходные проблемы

В исходном коде (см. Ниже) вы перезаписываете его с возвращением логического значения execute.

//old code (don't use this)
$result = $result->execute([
    ':user_email' => $user_email,
]);

//$result = TRUE|FALSE

if ($result->fetchColumn() !== false)
{
    return $result;
}

И затем вы пытаетесь вызвать метод логического типа, который хорошоне будет работать.Но проблемы глубже, чем это.Предположим, что вы не перезаписываете его.

//old code (don't use this)
$result->execute([
    ':user_email' => $user_email,
]);

//$result = PDOStatment object.

if ($result->fetchColumn() !== false)
{
    return $result;
}

Теперь результат по-прежнему ваше PDOStatement, что хорошо, но, как я уже сказал, вы не сохраняете извлеченные данные.На этот раз вы возвращаете объект PDOStatement.Это не то, что вы хотите.

Затем, как я уже говорил ранее, если вы сохраните его и вернете.Это все еще, вероятно, не то, что вы после.Потому что fetchColumn() обращается только к одной строке за раз и только к одному столбцу.

Но у меня нет возможности узнать, что вы хотите.Может быть, это то, что вы хотите?В этом случае ваш запрос меньше идеального.Может быть, вы просто хотите посмотреть, существует ли пользователь с данным адресом электронной почты?В этом случае я бы использовал этот запрос.

$result = $this->con->prepare("SELECT id FROM ".$table_name." WHERE username = :user_email");

$result->execute([
    ':user_email' => $user_email,
]);
//there is no need to check it (see below)
return $result->fetchColumn();

PDOStatement :: fetchColumn () возвращает один столбец из следующей строки набора результатов или FALSE, если строк больше нет.

Я также могу сказать по вашим материалам, что ваша установка БД, вероятно, неверна, то есть если вам действительно нужна динамическая таблица $table.Причина, по которой я могу это сказать, заключается в том, что вы не должны дублировать какие-либо пользовательские данные (или любые другие данные, это называется нормализацией), а наличие динамической таблицы означает, что электронная почта может существовать отдельно в двух (или более) таблицах.Если это не так, не делайте его динамичным.Почему это проблема, подумайте, что произойдет, если пользователь изменит свое «электронное письмо» сейчас, потому что оно существует в 2 таблицах (потенциально), вам придется обновлять его в обоих местах.Но хуже всего то, что это усложняет все, что вы делаете с электронными письмами.

Не видя схемы для ваших таблиц, я могу только догадываться об этом и о том, как это исправить.Но обычно вы используете внешний ключ и связываете запись пользователя с этим.Затем с помощью JOIN вы можете получить доступ к электронной почте без дублирования.

Тем не менее, есть несколько случаев, когда это может быть приемлемым, но я не могу узнать, так ли это в вашем случае.Просто краткий пример - отдельная таблица для пользователей и администраторов (в основном это две пользовательские системы).

Безопасность

Последнее, что нужно сделать очень очень осторожно:

"select * from ".$table_name." where username = :user_email"

Проблема здесь в том, что она открыта для SQL-инъекций.Каждый раз, когда вы объединяете переменную в SQL, вы открываете дверь для инъекционных атак.Ну, вы можете сказать, что я передаю в консервированной строке account.Что нормально, но в точке отказа нет проверки.Поэтому, возможно, через 5 месяцев вы повторно используете этот код и забудете, что никогда не проверяли имя таблицы.Может быть, и нет, но факт остается фактом: если пользовательские данные могут попасть в этот аргумент, у вас нет никакой защиты от инъекции в имя таблицы, вероятность этого есть.

Что-то столь же простое, как этот

  public function verify_user($table_name,$user_email){
     $allowed = ['account','users'];
     if(!in_array($table_name, $allowed )) throw new Exception('Invalid table name');
 }

Видите, теперь практически невозможно вставить что-либо в имя таблицы.Кроме того, потому что это тот же метод (в точке сбоя), вы никогда не потеряете эту защиту.Очень легко быть в спешке и скопировать кусок кода, изменить несколько вещей и… ну, вы знаете.

Только мои 3 цента.

ОБНОВЛЕНИЕ

Таким образом, даже если мала вероятность того, что пользовательский ввод может войти в $table, вы не можете гарантировать его 100%, поскольку из verify_user у вас нет возможности узнать, откуда поступили данные, но вы доверяете веречто это не пользовательский ввод.Что касается SQLInjection, вы не можете сказать, что это нормально, потому что я буду называть этот метод только определенным образом.Это должно быть 100% доказательство инъекции или настолько близко, насколько это возможно для человека.

Почему это важно, спросите вы?Представьте себе это.

   $userprofileobj->verify_user('account --',$_SESSION['user_email']);

Эти два маленьких -- похожи на // в PHP, но для SQL они закомментируют оставшуюся часть строки в SQL, поэтому ваш запрос становится таким.

"select * from account -- where username = :user_email"

Или (по существу)

"select * from account"

Итак, мы просто изменили то, что делает ваш запрос.Теперь, к счастью, на самом деле невозможно выполнить 2 запроса одновременно в PDO, вы можете сделать это (с небольшой работой) в MySqli.Но из соображений безопасности они в основном покончили с этой способностью.Причина в этом (или, что еще хуже, в создании пользователей БД).

  $userprofileobj->verify_user('account; DROP TABLE account --',$_SESSION['user_email']);

Что, если бы вы могли сделать 2 запроса, сделало бы это:

 SELECT * FROM account
 DROP TABLE account

В любом случае это опасная вещь,следует избегать любой ценой.Быть ленивым (а я ленивым программистом, так что не поймите это неправильно) - это не тот ответ, который вы хотите дать после того, как ваша база данных была скомпрометирована, и вы предоставили данные пользователя третьей стороне.Это просто не вариант.

Все это делает:

if(!in_array($table_name, ['table1', 'table2', ...])) throw new Exception('Invalid table name');

Выдает ошибку, если "needle" $table_name не находится в "haystack". Консервированный список имен таблиц.Поэтому, если я сделаю это (используя наш пример выше):

if(!in_array('account --', ['table1', 'table2', ...])) throw new Exception('Invalid table name');

Он не найдет account -- в нашем списке table1 и table2 и взорвется, тем самым предотвратив атаку Injection.

Приветствия.

0 голосов
/ 29 сентября 2018
$result = $this->con->prepare("select * from ".$table_name." where username = :user_email");

$result = $result->execute(..)

Вы перезаписываете результат $.$ This-> con> prepare (..) устанавливает $ result как оператор PDO (см. http://php.net/manual/en/pdo.prepare.php). Объект оператора PDO имеет метод -> execute (...), который возвращает логическое значение (true / false), а также метод -> fetchColumn (). Когда вы выполняете функцию execute (), вы перезаписываете свой объект оператора PDO с результатом выполнения execute (), которая является только логическим значением и не имеет методов вэто вообще так, поэтому $ result не имеет метода -> fetchColumn ().

...