PDO - 致命错误:在非对象上调用成员函数 fetch()

2022-01-20 00:00:00 fetch php mysql pdo

如果我尝试运行以下 PHP 代码,我会得到一个

if I try to run the following PHP code, I get a

在非对象上调用成员函数 fetch().

Call to a member function fetch() on a non-object.

你知道为什么吗?我在另一个网站上使用相同的代码,它工作得很好.

Do you know why? I use the same code on another site, where it works just fine.

<?php
$username = ($_GET ['user']);
try {
    $dbh = new PDO("mysql:host=localhost;dbname=***", '***', '***');    
} catch (PDOException $e) {
    echo $e->getMessage();
}
$sth = $dbh->query( "SELECT user, captcha 
    FROM xf_captcha WHERE user='$username'" );
print_r($sth->fetch());
?>

<小时>

$sth = $dbh->query( "SELECT username, user_state, last_activity, alerts_unread, conversations_unread, message_count 
    FROM xf_user WHERE username='$user'" );
$row = $sth->fetch();

<小时>

这看起来安全吗,我应该做更多吗?

Does this look safe, should I do more ?

<?php
$username = ($_GET ['user']);
try {
    $dbh = new PDO("mysql:host=localhost;dbname=***", '***', '***');
} catch (PDOException $e) {
    echo $e->getMessage();
}
$sth = $dbh->prepare("SELECT username, captcha, timestamp 
    FROM xf_captcha 
    WHERE username = :username", array(PDO::ATTR_CURSOR => PDO::CURSOR_FWDONLY));
$sth->execute(array(':username' => $username));
print_r($sth->fetch());
?>

推荐答案

你的代码在问题的顶部有变量 $username,但是你有 $user在底部.

Your code has the variable $username in the top part of your question, but you then have $user in the bottom section.

您是否打算使用相同的变量?

Are you perhaps meaning to use the same variable?

$username = ($_GET ['user']);
$sth = $dbh->query( "SELECT username, user_state, last_activity, alerts_unread, conversations_unread, message_count 
  FROM xf_user WHERE username='$user'" );
  //                           ^^ Should this ALSO be $username ?   
$row = $sth->fetch();

好的,现在你的 PDO::ATTR_EMULATE_PREPARES 很可爱.观察这一点:

Okay, now you are just being cute with your PDO::ATTR_EMULATE_PREPARES. Observe this:

数据库和表结构:

Database changed
mysql> show tables
    -> ;
+----------------+
| Tables_in_prep |
+----------------+
| users          |
+----------------+
1 row in set (0.00 sec)

mysql> select * from users;
+----+---------+--------+
| id | userid  | pass   |
+----+---------+--------+
|  1 | Fluffeh | mypass |
+----+---------+--------+
1 row in set (0.00 sec)

还有一些从你的复制而来的 PHP 代码,添加了 PDO 属性:

And some PHP code that is copied from yours, with the added PDO attribute:

<?php
    //$username = ($_GET ['user']);
    $username="Fluffeh";

    $dbh = new PDO('mysql:host=localhost;dbname=prep', 'prepared', 'example');
    $dbh->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_OBJ);

    $sth = $dbh->query( "SELECT userid, pass FROM users WHERE userid='$username'" );
    echo "Trying to use $username.
";
    print_r($sth->fetch());
    echo "----------------------------------------

";
?>

<?php
    //$username = ($_GET ['user']);
    $username="user2693017";

    $dbh = new PDO('mysql:host=localhost;dbname=prep', 'prepared', 'example');
    $dbh->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_OBJ);

    $sth = $dbh->query( "SELECT userid, pass FROM users WHERE userid='$username'" );
    echo "Trying to use $username.
";
    print_r($sth->fetch());
    echo "----------------------------------------

";
?>

<?php
    //$username = ($_GET ['user']);
    $username="Oh my' or 1=1 or 'm=m";

    $dbh = new PDO('mysql:host=localhost;dbname=prep', 'prepared', 'example');
    $dbh->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_OBJ);

    $sth = $dbh->query( "SELECT userid, pass FROM users WHERE userid='$username'" );
    echo "Trying to use $username.
";
    print_r($sth->fetch());
    echo "----------------------------------------

";
?>

<?php
    //$username = ($_GET ['user']);
    $username="(select id from users limit 1)";

    $dbh = new PDO('mysql:host=localhost;dbname=prep', 'prepared', 'example');
    $dbh->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_OBJ);

    $sth = $dbh->query( "SELECT userid, pass FROM users WHERE id='$username'" );
    echo "Trying to use $username.
";
    print_r($sth->fetch());
    echo "----------------------------------------

";
?>

<?php
    //$username = ($_GET ['user']);
    // Changed this one to be a non-string, you might be checking an ID instead.
    $username="(select id from users limit 1)";

    $dbh = new PDO('mysql:host=localhost;dbname=prep', 'prepared', 'example');
    $dbh->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_OBJ);

    $sth = $dbh->query( "SELECT userid, pass FROM users WHERE id=$username" );
    echo "Trying to use $username.
";
    print_r($sth->fetch());
    echo "----------------------------------------

";
?>

<?php
    //$username = ($_GET ['user']);
    $username="bob'; drop table users;   
    ";
    // This one is tricker to do in PHP code. I could easily enter this into a text field however.

    $dbh = new PDO('mysql:host=localhost;dbname=prep', 'prepared', 'example');
    $dbh->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_OBJ);

    //$sth = $dbh->query( "SELECT userid, pass FROM users WHERE id='$username'" );
    echo "Trying to use $username.
";
    print_r($sth->fetch());
    echo "----------------------------------------

";
?>

还有输出:

    Trying to use Fluffeh.
stdClass Object
(
    [userid] => Fluffeh
    [pass] => mypass
)
----------------------------------------


    Trying to use user2693017.
----------------------------------------


    Trying to use Oh my' or 1=1 or 'm=m.
stdClass Object
(
    [userid] => Fluffeh
    [pass] => mypass
)
----------------------------------------


    Trying to use (select id from users limit 1).
----------------------------------------


    Trying to use (select id from users limit 1).
stdClass Object
(
    [userid] => Fluffeh
    [pass] => mypass
)
----------------------------------------


    Trying to use bob'; drop table users;   
        .
----------------------------------------

哦,我把最后一个留到最后一个的原因是这个输出现在在我的数据库中:

Oh, the reason I left the last one till LAST is this output now in my database:

mysql> show tables;
Empty set (0.00 sec)

是的,没错,我刚刚丢了一张桌子.让我再说一遍,我有一个 select 语句,我用一个小技巧输入了一个值,任何有半脑和一些恶意意图的人都可以在文本字段中执行,然后删除你的表.

Yes, that's right, I just dropped a table. Let me say that again, I had a select statement, and with a little trickery I entered in a value that ANYONE with half a brain and some malicious intent could do into a text field, and DROPPED YOUR TABLE.

现在,当然,如果您设置正确,您可能会为 select 语句设置一个不同的用户,并且只授予他们数据库中的 select 权限,以停止这种事情正在发生 - 但老实说......你不是吗?

Now, granted, if you are setting things up properly, you might well set up a different user for the select statements, and only grant them select rights from your database, to stop this sort of thing happening - but lets be honest... you aren't are you?

显然设置模拟是不够的.说真的,现在请务必阅读该答案,如果您想在代码中保持安全,请使用准备好的语句并使用参数.

Clearly setting that emulation is not enough. Seriously, now PLEASE do go read that answer, use prepared statements and use params if you want to be secure in your code.

相关文章