0

So i'm trying to convert all of my SQL statements to prepared statements etc to prevent SQL injection attacks, but i'm having some issues fetching stuff etc

My code:

if($_GET["action"] == "ban"){

    if(isset($_GET["username"])){

        $username = $_GET["username"];

        $banMsg = $_GET["banMsg"];

        $email = "test@gmx.ch";

        $sql = "SELECT * FROM bans WHERE username = ?";
        $stmt = $db->prepare($sql);
        $stmt->bind_param("s", $username);
        $stmt->execute();
        $result = $stmt->fetch();
        $stmt->close();

        if($result->num_rows > 0){ //LINE 61
            die(json_encode(array("status" => 400, "message" => "User already banned")));
        }

        $result2 = $db->prepare("INSERT INTO bans (username, ip, email, message, expire, ban_creator) VALUES (?, ?, ?, ?, ?, ?)");
        $result2->bind_param("sssssd", $username, null, $email, $banMsg, null, 1); // LINE 72^^
        $result2->close();
        if($result2){
            updateBanCache();
            die(json_encode(array("status" => 200, "message" => "Successfully banned")));
        } else {

            die(json_encode(array("status" => 400, "message" => "SQL error")));
        }

    }

Also $result = $stmt->get_result(); doesn't wanna work for me, i do have mysqlnd driver installed in my php / cpanel though.

Any pointers would be helpful thanks!

ERROR LOG:

[11-Nov-2020 04:46:04 America/New_York] PHP Notice:  Trying to get property 'num_rows' of non-object in /home/public_html/index.php on line 61
[11-Nov-2020 04:46:04 America/New_York] PHP Fatal error:  Uncaught Error: Cannot pass parameter 3 by reference in /home/elysianmenu/public_html/index.php:72
Stack trace:
#0 {main}
  thrown in /home/public_html/index.php on line 72

SIDE NOTE: I also tried using $result = $stmt->get_result(); but I end up with error:

[11-Nov-2020 04:57:30 America/New_York] PHP Fatal error:  Uncaught Error: Call to undefined method mysqli_stmt::get_result() in /home/public_html/index.php:55
Stack trace:
#0 {main}
  thrown in /home/public_html/index.php on line 55

^^ Yes i do have the mysqlnd driver installed

user3840170
  • 26,597
  • 4
  • 30
  • 62
  • Your call to `$result2->bind_param("sssssd"` indicates 6 values and you only pass 5. Also check that you have the right types. – Nigel Ren Nov 11 '20 at 09:38
  • The errors i am getting is: [11-Nov-2020 04:23:55 America/New_York] PHP Notice: Trying to get property 'num_rows' of non-object in /home/public_html/forums/index.php on line 60 [11-Nov-2020 04:23:55 America/New_York] PHP Fatal error: Uncaught Error: Call to a member function bind_param() on boolean in /home/public_html/forums//index.php:71 Stack trace: #0 {main} thrown in /home//public_html/forums//index.php on line 71, thats the error i am getting line 71 is – Lucas Seer Nov 11 '20 at 09:41
  • Those details should be part of the question. – Nigel Ren Nov 11 '20 at 09:42
  • Sorry i forgot, edited it and added it – Lucas Seer Nov 11 '20 at 09:44
  • Also noticed i forgot to change from query to prepare, did that now and now i got new errors: [11-Nov-2020 04:46:04 America/New_York] PHP Notice: Trying to get property 'num_rows' of non-object in /home/public_html/forums/index.php on line 61 [11-Nov-2020 04:46:04 America/New_York] PHP Fatal error: Uncaught Error: Cannot pass parameter 3 by reference in /home/public_html/forums/index.php:72 Stack trace: #0 {main} thrown in /home/public_html/forums/index.php on line 72 – Lucas Seer Nov 11 '20 at 09:47
  • Please update your question to mark lines 61 and 72. – Ro Achterberg Nov 11 '20 at 09:55
  • See [this answer](https://stackoverflow.com/questions/371644/using-nulls-in-a-mysqli-prepared-statement/10340795) for the error on line 72. – Ro Achterberg Nov 11 '20 at 10:05
  • What am i looking for in this thread? – Lucas Seer Nov 11 '20 at 10:13
  • What have you tried to debug the problem? What's the reason for calling `close` within your code? Have you tried checking what `$result` contains in line 61? – Nico Haase Nov 11 '20 at 17:24
  • Does this answer your question? [Reference - What does this error mean in PHP?](https://stackoverflow.com/questions/12769982/reference-what-does-this-error-mean-in-php) – Nico Haase Nov 11 '20 at 17:25

2 Answers2

0

From the docs: Fetch results from a prepared statement into the bound variables.

fetch() returns either TRUE, FALSE or NULL, but not the result set you expected. Instead, it sets the output to the variables you previously bound (using bind_param()) by reference. This is why you have to use variables, and not actual scalar types.

If your query did not return any rows, fetch() will return NULL. Update your code as follows:

$stmt = $db->prepare($sql);
$stmt->bind_param("s", $username);
$stmt->execute();
if ($stmt->fetch() === TRUE)
    die(json_encode(array("status" => 400, "message" => "User already banned")));
$stmt->close();

And to fix the error on line 72, you have to pass the values by reference, using variables. Something like this:

$ip = NULL;
$expire = NULL;
$ban_creator = 1;

$result2->bind_param("sssssd", $username, $ip, $email, $banMsg, $expire, $ban_creator);

Don't forget to execute the query! You're checking $result2 before anything actually happened.

Ro Achterberg
  • 2,504
  • 2
  • 17
  • 17
  • I did what you said and did: $ip = NULL; $email = NULL; $expire = NULL; $result2->bind_param('sssssd', $username, $ip, $email, $banMsg, $expire, 1); $result2->execute(); $result2->close(); – Lucas Seer Nov 11 '20 at 10:32
  • But now i'm getting this error: PHP Fatal error: Uncaught Error: Cannot pass parameter 7 by reference in /home/public_html/index.php:66 – Lucas Seer Nov 11 '20 at 10:33
  • ;). We prefer to help you help yourself. I've updated my answer with a fix. Can you now spot why this worked? See also [this question](https://stackoverflow.com/questions/373419/whats-the-difference-between-passing-by-reference-vs-passing-by-value). – Ro Achterberg Nov 11 '20 at 11:44
  • Ah i see it was a issue with the ban creator okey. That solved it. – Lucas Seer Nov 11 '20 at 21:44
  • Good to know. If you found my answer helpful, then please consider upvoting it and marking it as the accepted answer :) – Ro Achterberg Nov 14 '20 at 14:32
0
  1. The action of banning a user MUST NOT come from a GET request ($_GET["action"]). This would make it incredibly simple for a webcrawler to stumble upon your banning script and ban all of your users (if it somehow found a list of usernames). The whole payload should be coming in as $_POST. The bottomline is: use $_POST when you are writing data, use $_GET when you are reading data.

  2. You MUST NOT blindly trust the user input. You should be validating the data even before connecting to the db. If the payload is invalid, no resources should be engaged.

  3. When you are only interested in the row count of a result set (and not the values in the result set), write COUNT(1) in your query. This way you can check the lone value to be zero or a non-zero value with no unnecessary overheads. Use something like this https://stackoverflow.com/a/51259779/2943403

  4. ip, expire, and ban_creator should have default settings in your table declaration of NULL, NULL, and 1. You should only mention those columns in an INSERT query if you wish to store a different value. Your INSERT query should only be binding 3 parameters. And of course check the outcome of the executed insert, like this: $stmt->execute() : How to know if db insert was successful?

mickmackusa
  • 43,625
  • 12
  • 83
  • 136