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
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.
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.
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.
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
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?
Related
I've made a PDO database class which I use to run queries on an MS Access database.
When querying using a date condition, as is common in SQL, dates are passed as a string. Access usually expects the date to be surrounded in hashes however. E.g.
SELECT transactions.amount FROM transactions WHERE transactions.date = #2013-05-25#;
If I where to run this query using PDO I might do the following.
//instatiate pdo connection etc... resulting in a $db object
$stmt = $db->prepare('SELECT transactions.amount FROM transactions WHERE transactions.date = #:mydate#;'); //prepare the query
$stmt->bindValue('mydate', '2013-05-25', PDO::PARAM_STR); //bind the date as a string
$stmt->execute(); //run it
$result = $stmt->fetch(); //get the results
As far as my understanding goes the statement that results from the above would look like this as binding a string results in it being surrounded by quotes:
SELECT transactions.amount FROM transactions WHERE transactions.date = #'2013-05-25'#;
This causes an error and prevents the statement from running.
What's the best way to bind a date string in PDO without causing this error? I'm currently resorting to sprintf-ing the string which I'm sure is bad practise.
Edit: if I pass the hash-surrounded date then I still get the error as below:
Fatal error: Uncaught exception 'PDOException' with message
'SQLSTATE[22018]: Invalid character value for cast specification:
-3030 [Microsoft][ODBC Microsoft Access Driver] Data type mismatch in criteria expression. (SQLExecute[-3030] at
ext\pdo_odbc\odbc_stmt.c:254)' in
C:\xampp\htdocs\ips\php\classes.php:49 Stack trace: #0
C:\xampp\htdocs\ips\php\classes.php(49): PDOStatement->execute() #1
C:\xampp\htdocs\ips\php\classes.php(52): database->execute() #2
C:\xampp\htdocs\ips\try2.php(12): database->resultset() #3 {main}
thrown in C:\xampp\htdocs\ips\php\classes.php on line 49
Normally when using a prepared statement or a parameterized query you don't need to worry about delimiting string and date values; all of that is handled for you "behind the scenes".
I just tried the following and it worked for me:
<?php
$connStr =
'odbc:Driver={Microsoft Access Driver (*.mdb, *.accdb)};' .
'Dbq=C:\\Users\\Gord\\Desktop\\Database1.accdb;' .
'Uid=Admin;Pwd=;';
$dbh = new PDO($connStr);
$sql =
"INSERT INTO tblDateTest (dateCol) VALUES (?)";
$newDateTimeValue = "2013-06-30 17:18:19";
$sth = $dbh->prepare($sql);
if ($sth->execute(array($newDateTimeValue))) {
echo "Done\r\n";
}
else {
$arr = $sth->errorInfo();
print_r($arr);
}
You should put your date (entire value) in the bindValue method. Example:
$stmt = $db->prepare('SELECT transactions.amount FROM transactions WHERE transactions.date = :mydate'); //prepare the query
$stmt->bindValue('mydate', '#2013-05-25#', PDO::PARAM_STR); //bind the date as a string
I've been trying to get prepared statements working - however, I keep running into the following error
<b>Fatal error</b>: Call to a member function bindParam() on a non-object on line <b>41</b><br />
I have copied exactly many tutorials and even the provided code did not work and threw the same error.
My code is below:
$mysqli = new mysqli(connect, username,pass, datatbase);
$name = 'Tester';
if (mysqli_connect_errno()) {
echo "Can't connect to MySQL Server. Errorcode: %s\n", mysqli_connect_error();
}
$stmt = $mysqli->prepare("INSERT INTO Parks VALUES (null,?,?,?,?,?,?,?,?,?,?,?,?,?,Now(),?,?,?, 0, 0, 0)");
if ($stmt === FALSE) {
die ("Mysql Error: " . $mysqli->error);
}
$stmt->bind_param('ssssssssssssssss', $name, $theme, $size, $mountains, $hills, $river, $lake, $island, $setofislands, $ocean, $waterfalls, $file, $image, $description, $author,$cs);
$stmt->execute();
$stmt->close();
$mysqli->close();
It's the BindParam Line causing the error.
thanks in advance :)
EDIT: Error resolved, however, no data is being inserted into the database.
EDIT: Updated query, database contains VARCHARs except for Description which is LONGTEXT. The final 3 are ints/doubles and there is a current date field.
bindParam is the PDO function. You are using mysqli so try bind_param instead. Where you have 'name' should also be the type definition, so you need 's' for string.
E.g:
$stmt->bind_param('s', $name);
Edit: Although saying that, the error doesn't say the function is incorrect. It says the object doesn't exist... Running this could would give you information as to why the prepare is failing.
$stmt = $mysqli->prepare("INSERT INTO 'Parks' VALUES(null, ?");
if ($stmt === FALSE) {
die ("Mysql Error: " . $mysqli->error);
}
Most likely the prepare is failing as the SQL is incorrect (My guess is the table name 'Parks' should NOT be in qutoes)
Edit 2: My guess for it still not working is:
$stmt->bindParam('name', $name);
Where you have 'name' should actually be the variable type, as in integer, double, string, etc. This is so the database knows what your variable is.
Try replacing that line with:
$stmt->bindParam('s', $name);
Inserting data into database with pdo prepared statment, doesnt work for me:
I use this function:
public function get_number_of_matches(){
$stmt = $this->pdo->prepare("INSERT INTO `words`( `word_name`, `word_count`, `search_id`) VALUES (:word, :count,:searchID)");
$stmt->bindParam(':word', $word);
$stmt->bindParam(':count', $count);
$stmt->bindParam(':searchID', $search_id);
for($i=0;$i<count($this->words);$i++){
if(preg_match_all('/'.$this->words[$i].'/i', $this->text,$matches)){
$count=count($matches[0]);
$word=$this->words[$i];
$search_id=1;
$stmt->execute();
break;
}
}
return 0;
}
Basically, I try to loop over the values and put them into the database.. no error is given.. nothing goes into the database ..why?
This is how I connect to the database:
class DBConnection {
public static $connect;
public static function connect(){
if(!isset(self::$connect)){
try{
self::$connect=new PDO('mysql:host=localhost;dbname=tweeter', 'root', '');
}catch(Exception $ex){
echo $ex->getMessage();
}
}
return self::$connect;
}
}
UPDATE
Also..see here:
I do the same thing with a different query..but when I try to put object properties inside a variable I get an error:
$tweet= $tweet->tweet ;
$user=$tweet->tweeter_name;
$link= $tweet->link;
Those variables go into a query:
$pdo= DBConnection::connect();
$stmt = $pdo->prepare("INSERT INTO `tweets`( `tweet`, `tweeter_name`, `link`, `date`, `search_id`) VALUES (:tweet, :tweeter_name, :link, :date, :search_id)");
$stmt->bindParam(':tweet', $tweet);
$stmt->bindParam(':tweeter_name', $user);
$stmt->bindParam(':link', $link);
$stmt->bindParam(':date', $date);
$stmt->bindParam(':search_id', $search_id);
I get errors like this:
Notice: Trying to get property of non-object in C:\xampp\htdocs\Twitter\demo.php on line 36
Notice: Trying to get property of non-object in C:\xampp\htdocs\Twitter\demo.php on line 37
Notice: Trying to get property of non-object in C:\xampp\htdocs\Twitter\demo.php on line 38
I can print the properties..but when allocating them to those binded variables..the above errors crop up
I get also this:
Fatal error: Uncaught exception 'PDOException' with message 'SQLSTATE[23000]: Integrity constraint violation: 1048 Column 'tweeter_name' cannot be null' in C:\xampp\htdocs\Twitter\demo.php:40 Stack trace: #0 C:\xampp\htdocs\Twitter\demo.php(40): PDOStatement->execute() #1 {main} thrown in C:\xampp\htdocs\Twitter\demo.php on line 40
I checked instead like this:
$tweet= "111111"; // $tweet->tweet ;
$user= "22222222"; // $tweet->tweeter_name;
$link= "3333333"; // $tweet->link;
$date= "444444";
and it worked..for some reason it hates those object properties ?!?
This should go as input:
RT #OrganicLiveFood: Scientists Warn #EPA Over #Monsanto's #GMO Crop Failures & Dangers #prop37 #labelGMO #yeson37 http://t.co/2XhuVxO8
Doumastic
TweetCaster for iOS
Mon, 19 Nov 2012 20:40:55 +0000
RT #OrganicLiveFood: Scientists Warn #EPA Over #Monsanto's #GMO Crop Failures & Dangers #prop37 #labelGMO #yeson37 http://t.co/2XhuVxO8
But it doesnt...?!?
Add self::$connection->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); right after connecting.
It would make sure PDO will throw PDOExceptions on every error, making them very easy to see. The error would then outline exactly what's wrong.
Check the return value from $stmt->execute(), if there was a problem it will return false and you should check $stmt->errorInfo() for details.
Or else use the ERRMODE_EXCEPTION that #Madara Uchiha suggests, but if you're not already handling exceptions in your application, this can be hard to adapt to.
Re: your update.
You should check error status from both PDO::prepare() and PDOStatement::execute() every time you call them. The error about "Trying to get property of non-object" likely means that $stmt is actually the boolean value false instead of a valid PDOStatement object. Your call to $stmt->bindParam() fails because false is not an object, so it cannot have a bindParam() method.
In my opinion it's much easier to pass parameters by value instead of binding variables by reference. Here's an example of both error-checking and parameters by value:
$pdo = DBConnection::connect();
$sql = "INSERT INTO `tweets`( `tweet`, `tweeter_name`, `link`, `date`, `search_id`)
VALUES (:tweet, :tweeter_name, :link, :date, :search_id)";
if (($stmt = $pdo->prepare($sql)) === false) {
die(print_r($pdo->errorInfo(), true));
}
$params = array(
':tweet' => $tweet,
':tweeter_name' => $user,
':link' => $link,
':date' => $date,
':search_id' => $search_id
);
if (($status = $stmt->execute($params) === false) {
die(print_r($stmt->errorInfo(), true));
}
The error "Column 'tweeter_name' cannot be null'" that you saw in the exception means that your tweeter_name column is declared NOT NULL, but your $user variable had no value when you bound it to the :tweeter_name parameter.
This question already has answers here:
mysqli::query(): Couldn't fetch mysqli
(4 answers)
Closed 9 months ago.
I started using mysqli_* functions instead of the old mysql_* functions in PHP, and I'm having a bit of a problem with this code:
public function addUser($table, $code = false, $rows = false) {
if (!$code) {
header("Location: " . $this->authenticate());
} else {
$this->getToken($code);
}
$user = $this->getEndpoint('users/current', false, true);
$user = $user->response->user;
if (!$rows)
$rows = array(
"remote_id" => $user->id,
"firstName" => $user->first_name,
"lastName" => $user->last_name,
"photo" => $user->photo->medium,
"gender" => $user->gender == 'male' ? 1 : 2,
"email" => $user->contact->email,
);
$rows['access_token'] = $this->accessToken;
$stmt = $this->mysql->prepare("SELECT id FROM users WHERE access_token = '{$this->accessToken}'"); //line 136
$stmt->execute(); //line 137
}
The code returns these 2 errors:
Warning: mysqli::prepare(): Couldn't fetch MySQL in C:\Users\Grega\Server\application\inc\classes\APIConnect.php on line 136
Fatal error: Call to a member function execute() on a non-object in C:\Users\Grega\Server\application\inc\classes\APIConnect.php on line 137
What is the reason for 'Couldn't fetch MySQL'? The database connection is correct, it works in other classes, and the query returns a valid result, if I echo it and execute it in phpMyAdmin. Also, my variable is named mysql NOT mysqli!
You should read more about the difference between MYSQL to MYSQLi.
While your code is:
$stmt = $this->mysql->prepare("SELECT id FROM users WHERE access_token = '{$this->accessToken}'");
You should do it like this:
$stmt = $this->mysql->prepare("SELECT id FROM users WHERE access_token = ?");
$stmt->bind_param("s" , $this->accessToken ); //Used 's' as I guess that the accessToken is a string
The binding part is the critical part of the prepare thing. (Your queries are safe)
After that you can use $stmt->execute(); and get_result().
For the first error: you probably closed the connection somewhere before this code, and this is why ($stmt -> close() )
For the second error: when you use (prepare()), you first introduce a SQL template to the database, which then has to pass the parameters to the "bind_param()" method to send it to the database with another protocol (to prevent SQL injection) and reduce the request and attach the parameters to SQL with execute() method
I am working with PHP-PDO and Oracle 11g. I am working with Oracle packages which have many functions and stored procedures. Now when I make a call to one of the functions from sql*plus or sql developer IDE, I run this command to get the result set.
select package_name.function_name(param1,param2) from dual
It works fine and returns my result set. Now when I do the same, I am getting errors from the PDO Exception handling. The code with on PHP end looks like this,
$stmt = "select package_name.function_name (?,?) from dual";
$res = $this->ConnOBJ->prepare($stmt);
$param1 = '1';
$param2 = null;
$result->bindParam(1,$param1);
$result->bindParam(2,$param2);
$result->execute();
And I get back an exception which is being logged into my log file.
Uncaught exception 'PDOException' with message 'SQLSTATE[HY000]: General error: 904 OCIStmtExecute: ORA-00904: "PACKAGE_NAME"."FUNCTION_NAME"": invalid identifier (/var/www/php-5.3.3/ext/pdo_oci/oci_statement.c:146)' in /opt/web/dir/ora_class.php:209 Stack trace: #0 /opt/web/dir/ora_class.php(209): PDOStatement->execute() #1 /opt/web/dir/ora_class.php(298): dbPDO->execPackage() #2 {main} thrown in /opt/web/dir/ora_class.php on line 209
Am I passing the query in a wrong way? Or am I binding the parameters in a wrong way?
Update
I have now got the data going through to Oracle, and have found how to pass null values. My code now is
$stmt = "select package_name.function_name(?,?) from dual";
$res = $this->ConnOBJ->prepare($stmt);
$param1 = 1;
$param2 = null;
$res->bindParam(1,$param1,PDO::PARAM_INT);
$res->bindParam(2,$param2,PDO::PARAM_NULL);
$res->execute();
var_dump($res->fetchAll());
And now when I pass data, I get back the error
PHP Fatal error: Uncaught exception 'PDOException' with message 'SQLSTATE[HY000]: General error: 932 OCIStmtFetch: ORA-00932: inconsistent datatypes: expected CHAR got DTYCWD (/var/www/php-5.3.3/ext/pdo_oci/oci_statement.c:467)' in /opt/web/dir/ora_class.php:216 Stack trace: #0 /opt/web/dir/ora_class.php(216): PDOStatement->fetchAll() #1 /opt/web/dir/ora_class.php(305): dbPDO->execPackage() #2 {main} thrown in /opt/web/dir/ora_class.php on line 216
I am making sure all the types are right, but I still am getting back the same error. I even removed the null value and passed in a string, and changed the pdo type to PDO::PARAM_STR, but it still gives me the error.
Does the function take one parameter or two? In SQL*Plus, you're passing two parameters. In PHP, you're passing only one. If the function requires two parameters and there is no overloaded method that takes only one parameter, you'd get this error.
I am not using PDO anymore, I would be using OCI drivers. Thank you for all the help.
Here is a link to an answer for a similar question [LINK] : https://stackoverflow.com/a/57558306/7897970
or best
//Your connection details
$conn = oci_connect($username, $password, '(DESCRIPTION = (ADDRESS = (PROTOCOL = TCP)(HOST = 127.0.0.1)(PORT = 1521))(CONNECT_DATA = (SERVER = DEDICATED) (SERVICE_NAME = XE)))' );
/* Your query string; you can use oci_bind_by_name to bind parameters or just pass the variable in it*/
$query = "begin :cur := functionName('".$param1."','".$param2."','".$param3."'); end;";
$stid = oci_parse($conn, $query);
$OUTPUT_CUR = oci_new_cursor($conn);
oci_bind_by_name($stid, ':cur', $OUTPUT_CUR, -1, OCI_B_CURSOR);
oci_execute($stid);
oci_execute($OUTPUT_CUR);
oci_fetch_all($OUTPUT_CUR, $res);
// To get your result
var_dump($res);