Executing a prepared PDO statement with the like clause [duplicate] - php

This question already has answers here:
How do I create a PDO parameterized query with a LIKE statement?
(9 answers)
Closed 3 years ago.
I am new to PHP, and am trying to learn to use PDO to connect to a test MySQL db. I have the following:
try {
$db = new PDO('mysql:dbname=MYDBNAME;host=MYHOST', 'USERNAME', 'PASSWORD');
$query = "select * from books where ? like '%?%'";
$stmt = $db->prepare($query);
$stmt->execute(array($searchtype, $searchterm));
} catch(PDOException $e) {
echo 'PDOException: ' . $e->getMessage();
}
When I try it I get the following warning:
Warning: PDOStatement::execute() [pdostatement.execute]: SQLSTATE[HY093]: Invalid parameter number: number of bound variables does not match number of tokens
When I remove the like clause, and the $searchterm param, it returns the result properly. I thought -- like '%?%' -- might not be a legal way to create this query under double quotes, so I tried escaping ', which did not work. I looked around for a solution, and found that someone moved '% and %' down to where $searchterm is:
$query = "select * from books where ? like ?";
...
$stmt->execute(array($searchtype, '\'%'.$searchterm.'%\''));
I got the same result.
Any help is appreciated. Thanks!
/ UPDATE ****/
I found on example 12 of http://us3.php.net/manual/en/pdo.prepared-statements.php
Example #12 Invalid use of placeholder
<?php
$stmt = $dbh->prepare("SELECT * FROM REGISTRY where name LIKE '%?%'");
$stmt->execute(array($_GET['name']));
// Below is What they suggest is the correct way.
// placeholder must be used in the place of the whole value
$stmt = $dbh->prepare("SELECT * FROM REGISTRY where name LIKE ?");
$stmt->execute(array("%$_GET[name]%"));
?>
I tried this, and even though I no longer get a Warning, I do not get any results. However when I execute the query directly I will get a couple of results. Any thoughts?

Don't add the quotes when binding prepared variables and dont bind the column name
$query = sprintf( "select * from books where %s like ?", $searchtype );
...
$stmt->execute(array($searchtype, '%'.$searchterm.'%'));

$stmt->execute(array($searchtype, '\'%'.$searchterm.'%\''));
This isn't how parameterised queries work. Inserted parameters act as literal strings already, you don't have to add quote delimiters around them or escape them (that's the whole point), and if you try, you're literally comparing against the string single-quote-searchterm-single-quote.
Consequently if you are (as I suspect) intending to compare a particular column against a literal string, you don't parameterise the column name. At the moment you are comparing a literal string to another literal string, so it'll either always be true or always false regardless of the data in the row!
So I think what you probably mean is:
$query= "SELECT * FROM books WHERE $searchtype LIKE ?";
$like= "%$searchterm%";
$stmt->execute(array($like));
thought naturally you will have to be very careful that $searchtype is known-good to avoid SQL-injection. Typically you would compare it against a list of acceptable column names before using it.
(Aside: there is a way of putting arbitrary strings in a schema name that you can use for a column, but it's annoying, varies across databases and there isn't a standard escaping function for it. In MySQL, you backslash-escape the backquote character, quotes and backslashes and surround the name with backquotes. In ANSI SQL you use double-quotes with doubled-double-quotes inside. In SQL Server you use square brackets. However in reality you vary rarely need to do any of this because really you only ever want to allow a few predefined column names.)
(Another aside: if you want to be able to allow $searchterm values with literal percents, underlines or backslashes in—so users can search for “100%” without matching any string with 100 in—you have to use an explicit escape character, which is a bit tedious:)
$query= "SELECT * FROM books WHERE $searchtype LIKE ? ESCAPE '+'";
$like= str_replace(array('+', '%', '_'), array('++', '+%', '+_'), $searchterm);
$stmt->execute(array("%$like%"));

The problem I see is if you had written a wrapper for PDO, then you would have to somehow handle this separately. The answer I had found and loved was write your query and concat the % to the parameter. i.e. "WHERE column like concat('%', :something, '%')"

Related

Protect Oracle database against SQL Injection

I'm on Symfony and I don't know how protect my database against sql injection. If you have some idea, I will be gratefull.
My function with sql :
public function getResult($$value)
{
$sql = "SELECT SOMETHING FROM SOMETHING smt
WHERE smt.THING = '".$value."'";
return $this->egee->executeQuery($sql);
}
And here is my executeQuery funciton :
public function executeQuery($sql) {
$entityManager = $this->em->getConnection('xxx');
$stmt = $entityManager->prepare($sql);
$stmt->execute();
return $stmt->fetch();
}
I allready try with BindParam, but it's didn't work with Oracle.
With BindParam I have this response :
Error 503 : Service Unavailable
The server is temporarily unable to service your request due to maintenance downtime or capacity problems. Please try again later.
Here's how you do it ... with any and every database: parameterized queries.
Your SQL string now becomes:
SELECT SOMETHING FROM SOMETHING smt WHERE smt.THING = ?
Notice the ? (which is not in quotes ... this is not a one-character literal string) This indicates a query parameter.
Now, each time you execute the query, you supply an array() containing each of the parameter-values you want to substitute, in order left-to-right. Different values may be used each time the query is executed (without re-preparing it), because these values are not "part of" the query: they are inputs.
No matter what the parameter-value contains, the database engine will never see it as anything other than the numeric or string value that it is. It will never regard it as "part of the SQL." Thus, SQL-injection becomes impossible.
Furthermore, the [binary] value is used directly, instead of being decoded from a character string. So, say, if you want to use quote-marks as part of your string parameter-value, you would not "encode" them with backslashes. (If you provided \", then "a backslash followed by a quote mark" is what SQL would see as the parameter's value ... a perfectly acceptable two-character value.)
Here's a nice write-up: https://www.w3schools.com/php/php_mysql_prepared_statements.asp
The documentation for Doctrine ORM in the Symfony manual shows an example of using a query parameter:
https://symfony.com/doc/current/doctrine.html#querying-with-sql
$sql = '
SELECT * FROM product p
WHERE p.price > :price
ORDER BY p.price ASC
';
$stmt = $conn->prepare($sql);
$stmt->execute(['price' => $price]);
You don't need to use BindParam. Just pass a hash array to execute(), where the hash keys are the named query parameter placeholders you put in your SQL query.

mysqli_stmt_bind_param() data not supplied when a question mark is a part of the query

Similar questions have been asked many times, but after reading almost every of these for over 5 hours, i have not found a suitable reply for my problem.
Im not an experience php / mysql developer, but i ve managed similar situations with the use of mysqli_stmt_bind_param() func.
Here is the query:
$query = 'SELECT Recipes.* , Categories.* FROM `Recipes` JOIN `Categories` ON JSON_EXTRACT(Recipes.category, \'$.category\') = \'Category ?\' WHERE Categories.category = ?';
I use this php code:
if ($stmt = mysqli_prepare($dbManager->getDBInstance(), $query)){
mysqli_stmt_bind_param($stmt,"ii", $id, $id);
}
Because i have a model on the client side like:
{
"category" : "...",
"recipes" : [{...},{...}]
}
The error is: Fatal error: Uncaught mysqli_sql_exception: No data supplied for parameters in prepared statement
I have already made similar queries with many more parameters, without any error: however, this is the first time i use the JSON_EXTRACT func from mysql.
I believe the error is caused by the $. which is not escaped correctly. The parameters to be replaced reference to the same variable, $id, which is an integer, and gets used for string interpolation in the first case ('Category 1') and as number after the WHERE clause.
Consider that, by not using mysqli_stmt_bind_param, the same query on phpmyadmin returns what i want, but that would open my code to mysql injections, which i want to avoid.
Also, notice that if i pass just one parameter to the function, the script gets executed (with wrong results), like if the query gets truncated at some point... i properly escaped every single quote, and even tried with double quotes, but the error is always the same.
Any hint on how to prevent the injection and achieve the result would be highly appreciated, because i really can't figure it out by myself.
Thank you
You have two parameters in the call to mysqli_stmt_bind_param(), but there's only one placeholder in $query. The first ? is inside quotes, so it's treated literally, not as a placeholder.
You can use CONCAT() to concatenate a string literal with a placeholder, so change it to:
$query = '
SELECT Recipes.* , Categories.*
FROM `Recipes`
JOIN `Categories` ON JSON_EXTRACT(Recipes.category, \'$.category\') = CONCAT(\'Category \', ?)
WHERE Categories.category = ?';
A placeholder can represent a complete data literal only. To put it simple - anything you would write in quotes (or a number). So it shouldn't be 'Category ?' but just ? where Category could be concatenated in PHP.
$query = 'SELECT * FROM `Recipes` JOIN `Categories` ON
JSON_EXTRACT(Recipes.category, \'$.category\') = ?
WHERE Categories.category = ?';
$stmt = mysqli_prepare($dbManager->getDBInstance(), $query);
$category = "Category $id";
mysqli_stmt_bind_param($stmt,"si", $category, $id);

Variable in Quotes in Postgres Query

This is probably a stupid question, but I have been Googling an answer for the better part of the day and can't get anywhere. I am trying to get the following bit of code to work, but can't find any help on how to properly format a prepared Postgres request in PHP.
$foo = $_GET[bar];
echo $foo; // 5555
//what I'm trying to do:
pg_prepare($dbconn,"table_query","SELECT Members FROM programs WHERE programID = '$1' ");
pg_execute($dbconn,"table_query", array($foo));
If I hardcode the statement with a value, it works fine, but only if I include the single quotes. I've tried just about every method I can find to escape the single quotes or append the quotes to the string, but all I can get are parsing errors.
What totally obvious thing am I missing?
Edit: Changed the snippet to clarify that the variable I am getting does not include quotes. Any method I where I try to add the quotes fails.
Let’s study a complete example. Suppose you got your value from a GET query which set the name pid. From your example query I expect the value to be the decimal representation of an integer, different from zero. It is a string, since nothing else can come from a GET query.
$pid = $_GET['pid'];
// This is _very_ important.
// Anything that comes from outside must be validated or sanitized.
// FILTER_VALIDATE_INT refuses "0" too (correct if needed).
if (filter_var($pid, FILTER_VALIDATE_INT) === false) {
// Deal with invalid input
}
$result = pg_query_params($dbconn,
'SELECT Members FROM programs WHERE programID = $1',
array($pid)
);
pg_query_params binds $1 with $pid and quotes it correctly, while you cannot use double quotes around the statement because PHP would expand $1 incorrectly. There is no need to put quotes around $pid manually, because pg_query_params takes care of this. Furthermore, PostgreSQL accepts an integer value both with quotes and without them, so fumbling with quotes is pointless in this case.
Instead of using the traditional pg_ functions, you might use PDO, the PHP Database Object abstraction layer.
In that case (disregarding possible options needed in your case):
$dsn = 'pgsql:host='. $host .';port=5432;dbname='. $database;
$dbh = new PDO($dsn, $user, $password);
$dbh->prepare('SELECT Members FROM programs WHERE programID = ?');
$result = $dbh->execute(array($pid)); // $pid as before
You should be using prepared statements. This should solve your quoting problem and also remove a major risk of SQL injection. Try something like this:
$stmt = $conn->prepare("SELECT Members FROM programs WHERE programID = ?");
$stmt->bind_param("s", $foo);
$foo = "5555";
$stmt->execute();

PHP PDO mysql prepared statment and join

I have a question.
I have the following query:
$query = "select * from module,bloc where module.id_bloc = ?";
I tried to bind the value so I did:
$stmt = $this->db->prepare($query);
$stmt->bindValue(1, "bloc.id_bloc");
But, when I test I don't get any result on my browser.
It's weird because when I replace directly inside like the following code:
$query = "select * from module,bloc where module.id_bloc = bloc.id_bloc";
I get the the right result on my browser.
Could someone explain to me why it doesn't work when I am doing a bindValue?
It will not work because, when bound, a string will be quoted. (Or, for all intents and purposes, work as if it were quoted, however PDO may handle it behind the scenes.) Then, your query is interpreted as:
select * from module,bloc where module.id_bloc = 'bloc.id_bloc'
That is: It will be interpreted as a literal string, rather than a reference to a table column, and will obviously not give you the expected result. There is no need for binding it to begin with.
If, for some reason, you need to run a query with a variable table/column name from an unsafe source, you will have to manually format/sanitize it; see here for an example of how to do it.

php preg_match_all avoid quotation mark

inside Moodle core, when sending a query to the database there's a call to preg_match_all looking for : (colon), in order to find query's parameters.
I have a string (inside " ") composed of digits, a colon and a letter ("102516101:t").
Of course it's not impling for a parameter. still the Moodle expects one because of the colon(:).
How can I prevent preg_match_all looking inside a Quotation mark?
or has anyone gut another idea?
According to the comments left above, you should explicity specify the variables used in your SQL statement. In other words you should not be constructing your SQL statement by hand, or the minimum required.
Example:
$sql = "SELECT * FROM {groups} WHERE name = :name";
$params = array('name' => '102516101:t');
$DB->execute($sql, $params);
You'll also note that table names are specified like this: {table_name}, they are automatically expanded with the right prefix.

Categories