PHP Prepared MySQL statments have started adding quote marks - php

So I have been using prepared statements for a while and for a number of projects and it has been a really good clean way to interact with the MySQL db, but today I have come across a strange problems.
My prepared statement has started adding extra ' to the sql statements and for the life of me I have no idea why...
so here is the code:
<?php
$sortby="ORDER BY submit_date DESC";
$offset = 3;
$sql = "SELECT img_id, img_name, submit_date FROM tbl_images WHERE img_active='y' :sortby LIMIT :offset, 9";
$stmt = $this->_db->prepare($sql);
$stmt->bindParam(":sortby", $sortby, PDO::PARAM_STR);
$stmt->bindParam(":offset", $offset, PDO::PARAM_INT);
$stmt->execute();
?>
so the above doesnt return anything, so looking at the database logs, this is what the query looks like
SELECT img_id, img_name, submit_date FROM tbl_images WHERE img_active='y' 'ORDER BY submit_date DESC' LIMIT 3, 9
it seems to have put an extra set of ' ' around the "ORDER BY submit_date DESC", but yet hasnt around the offset?
Can anyone spot the problem as its driving me mad :)
Thank you in advance!
Solution, thanks to the guys that posted, you were correct, I split the fields out to parts and works like a charm. Code solution below:
<?php
$sortfield="submit_date";
$sortway="DESC"
$offset = 3;
$sql = "SELECT img_id, img_name, submit_date FROM tbl_images WHERE img_active='y' ORDER BY :sortfield :sortway LIMIT :offset, 9";
$stmt = $this->_db->prepare($sql);
$stmt->bindParam(":sortfield", $sortfield, PDO::PARAM_STR);
$stmt->bindParam(":sortway", $sortway, PDO::PARAM_STR);
$stmt->bindParam(":offset", $offset, PDO::PARAM_INT);
$stmt->execute();
?>

Have a look at the documentation for mysqli_stmt::prepare:
The markers are legal only in certain places in SQL statements. For example, they are allowed in the VALUES() list of an INSERT statement (to specify column values for a row), or in a comparison with a column in a WHERE clause to specify a comparison value.
Basically, anything structural to the query is not allowed to be a bound parameter. Only data can be sent in this way.
PDO's prepared statements work in effectively the same way. In your case, however, PDO is a bit stupid, because it's running in "emulate prepares" mode (which is the default, but you should turn it off to get the most from PDO). It basically does all the substitution itself, rather than sending the query and the data to the server separately. It sees that the data is a string and thinks "aha, a string: I need to put quotes around this." You therefore end up with your malformed query.
The solution is not to build up structural parts of your query with bound parameters. Either substitute them in yourself with concatenation, or (and this is better) have alternative query strings for different settings. This is the most secure way: anything involving concatenation is a recipe for insecurity.
Oh, and turn PDO emulate prepares off!

With PDO you shouldn't use prepared statement binding variable substitution outside the WHERE clause or an ON clause. If you do they--any string--will get quoted (as they should). While the $offset integer binding might work, you shouldn't do it. You should just substitute the value with a string (after comparing it to a whitelist array of valid values).
$sql = "SELECT img_id, img_name, submit_date FROM tbl_images WHERE img_active='y' $sortby LIMIT $offset, 9";

You want to string interpolate $sortby, rather than bind it as an escaped and quoted SQL literal.
(But take care not to interpolate untrusted SQL fragments!)
Parameter binding is for the substitution of literal values into queries, by which we usually mean plain numbers or strings. Parameters are not for SQL identifiers (like table or column names) nor for syntactic elements.
PDO is interpreting $sortby as a literal string, which is what you asked it to do:
SELECT ... WHERE image_active='y' 'literal string substituted here' ...
You're certainly generating a syntax error with that query.
Confusing matters somewhat is that MySQL does allow placeholders for arguments to LIMIT clauses. This is quite convenient, but surprising to those familiar with other RDBMSs.

Related

query multiple columns php/mysql

new to php and am enrolled on a course, so can ask tutor tomorrow if this is more complicated than i think it might be!
I have an sql query, and it works fine. But I am trying to add and 'and' in the select statement.
This is what I have at the minute
$query = "SELECT * from table1 where table1.age <= " . $_POST['min_age'] ;
I have a 'region' input on my linked html page and want results to be returned only if the min_age and region values match those inputted by the user.
I have tried adding an 'and where' but it doesn't work and I am not sure if it is because of the multiple "'s or if what I am trying to do needs a different method?
Thanks
If you need multiple conditions, just separate them with AND:
... WHERE table1.age <= ? AND table1.region = ?
No need to use WHERE again. Just like you wouldn't need to use if() more than once if you were writing a complex condition in PHP.
PS: This isn't directly related to your question, but you should get into the habit of not putting $_POST or $_GET variables directly into your SQL queries. It's a good way to get hacked! Ask your tutor about "SQL injection," or read my presentation SQL Injection Myths and Fallacies.
I know you're just starting out, but if you were training to be an electrician, you would place a high priority on learning how to avoid being electrocuted or how to avoid causing a fire.
Here's how I would write your query using mysqli. One advantage of using query parameters is you never need to worry about where you start and end your quotes.
$query = "SELECT * from table1 where table1.age <= ? AND table1.region = ?";
$stmt = $mysqli->prepare($query) or trigger_error($mysqli->error, E_USER_ERROR);
$stmt->bind_param("is", $_POST["min_age"], $_POST["region"]);
$stmt->execute() or trigger_error($stmt->error, E_USER_ERROR);
The other good habit I'm showing here is to always report if prepare() or execute() return an error.
If you must interpolate variables into your SQL, first make sure you protect the variables either by coercing the value to an integer, or else by using a proper escaping function like mysqli_real_escape_string(). Don't put $_POST variables directly into the string. Also you don't have to stop and restart the quotes if you use PHP's syntax for embedding variables directly in double-quoted strings:
$age = (int) $_POST["min_age"];
$region = $mysqli->real_escape_string($_POST["region"]);
$query = "SELECT * from table1 where table1.age <= {$age}
AND table1.region = '{$region}'";

PHP: using prepared statements and protecting against SQL injection vs escape

I do understand that the prepared statements is the ultimate way to seek protection against the SQL injection. However, they provide coverage in a limited fashion; for example, in cases where I let the user to decide how the order by operation to be ( i.e, is it ASC or DESC? etc ), I get no coverage there with the prepared statements.
I understand that I can map the user input to a pre-defined white list for that. But, this is only possible when a whitelist can be created or guessed thoroughly beforehand.
For example, in the cases I mention above ( the ASC, or DESC ), this can easily be mapped and verified against a list of accepted values. But isn't there a situation where the portion of the SQL statement cannot be verified against a white list?
If such a situation exists, then what's the recommended approach?
If I were to escape the user_input using the underlying database's built-in escape utility (such as mysqL_real_escape_string for mysql) across the board, where would I fail?
I'm asking this question with the assumption that I always construct my sql statements with quoted values - even for integers...
Let's take a look at the following example and reflect upon it..
select {$fields} from {$table} where Age='{$age}' order by {$orderby_pref}
Assume all vars are user supplied.
If I were to mysql_real_escape_string all the variables in the above SQL ( as opposed to using prepared statements which covers me only half-way forcing me to come up whitelists for the other half that it cannot help), wouldn't it be equally safe (and easier to code)? If not, in which input scenario escape utility would fail?
$fields = mysql_escape($fields);
$table = mysql_escape($table);
$age = mysql_escape($age);
$orderby_pref = mysql_escape($orderby_pref);
select {$fields} from {$table} where Age='{$age}' order by {$orderby_pref}
You always need to use white-lists for stuff like table- or column names, whether you use prepared statements or the mysql escape functions.
The problem is that table names and column names are not quoted in single or double quotes, so if you use a function that specifically quotes these characters (and some more of course...), it will do nothing for your table name.
Consider the table name my_table; DELETE * FROM mysql; SELECT * FROM my_table. Nothing in this string will get escaped by mysql's escape functions but it is definitely a string you would want to check against a white-list.
Apart from that the mysql escape functions have a problem with character sets that can render them useless, so you are always better off with prepared statements.
You could use PDO and your life will get easier ... :
# Order
switch(strtoupper($Order)){
default:
case 'ASC':
$Order = 'ASC';
break;
case 'DESC':
$Order = 'DESC';
break;
}
# ID
$ID = 39;
$Username = 'David';
# Query
$Query = $this->DB->Main->prepare('SELECT * FROM Table WHERE ID = :ID AND Username = :Username ORDER BY HellBob '.$Order);
$Query->bindValue(':ID', $ID, PDO::PARAM_INT);
$Query->bindValue(':Username', $Username, PDO::PARAM_STR);
# All good ?
if(!$Query->execute()){
exit('Error');
}
// Results
$Row = $Query->fetch(PDO::FETCH_ASSOC);
You don't have to worry about quotes or SQL injections. You can use simple "white list" as you mention to get variable into your query.

PDO quote method

Where and when do you use the quote method in PDO? I'm asking this in the light of the fact that in PDO, all quoting is done by the PDO object therefore no user input should be escaped/quoted etc. This makes one wonder why worry about a quote method if it's not gonna get used in a prepared statement anyway?
When using Prepared Statements with PDO::prepare() and PDOStatement::execute(), you don't have any quoting to do : this will be done automatically.
But, sometimes, you will not (or cannot) use prepared statements, and will have to write full SQL queries and execute them with PDO::exec() ; in those cases, you will have to make sure strings are quoted properly -- this is when the PDO::quote() method is useful.
While this may not be the only use-case it's the only one I've needed quote for. You can only pass values using PDO_Stmt::execute, so for example this query wouldn't work:
SELECT * FROM tbl WHERE :field = :value
quote comes in so that you can do this:
// Example: filter by a specific column
$columns = array("name", "location");
$column = isset($columns[$_GET["col"]]) ? $columns[$_GET["col"]] : $defaultCol;
$stmt = $pdo->prepare("SELECT * FROM tbl WHERE " . $pdo->quote($column) . " = :value");
$stmt->execute(array(":value" => $value));
$stmt = $pdo->prepare("SELECT * FROM tbl ORDER BY " . $pdo->quote($column) . " ASC");
and still expect $column to be filtered safely in the query.
The PDO system does not have (as far as I can find) any mechanism to bind an array variable in PHP into a set in SQL. That's a limitation of SQL prepared statements as well... thus you are left with the task of stitching together your own function for this purpose. For example, you have this:
$a = array(123, 'xyz', 789);
You want to end up with this:
$sql = "SELECT * FROM mytable WHERE item IN (123, 'xyz', 789)";
Using PDO::prepare() does not work because there's no method to bind the array variable $a into the set. You end up needing a loop where you individually quote each item in the array, then glue them together. In which case PDO::quote() is probably better than nothing, at least you get the character set details right.
Would be excellent if PDO supported a cleaner way to handle this. Don't forget, the empty set in SQL is a disgusting special case... which means any function you build for this purpose becomes more complex than you want it to be. Something like PDO::PARAM_SET as an option on the binding, with the individual driver deciding how to handle the empty set. Of course, that's no longer compatible with SQL prepared statements.
Happy if someone knows a way to avoid this difficulty.
A bit late anwser, but one situation where its useful is if you get a load of data out of your table which you're going to put back in later.
for example, i have a function which gets a load of text out of a table and writes it to a file. that text might later be inserted into another table. the quote() method makes all the quotes safe.
it's real easy:
$safeTextToFile = $DBH->quote($textFromDataBase);

Why can't I write PDO prepared statements in quotes?

I had the following piece of code with PDO prepared statements:
$stmt = $conn->prepare('SELECT `myColumn1` FROM my_table '.
'WHERE `myColumn2`=:val LIMIT 1');
$stmt->bindValue(":val", $value);
$stmt->execute();
$row = $stmt->fetch(PDO::FETCH_ASSOC);
This works fine. It sends the following query:
113 Query SELECT `myColumn1` FROM my_table WHERE `myColumn2`=":val" LIMIT 1
and it returns the correct value.
But it doesn't work if I change the first line to
$stmt = $conn->prepare('SELECT `myColumn1` FROM my_table '.
'WHERE `myColumn2`=":val" LIMIT 1');
or
$stmt = $conn->prepare('SELECT `myColumn1` FROM my_table '.
'WHERE `myColumn2`=':val' LIMIT 1');
The same query is sent, but PDO returns false.
Can anybody explain why?
From the page you quote:
The parameters to prepared statements don't need to be quoted; the driver automatically handles this.
The purpose of the quotation marks is to delimit string data from the rest of the query, since it cannot easily be separated (unlike numbers, which have an obvious format). Since using prepared statements means that query and data are passed separately, the quotes are unnecessary.
One of the advantages of prepared statements are that types are handled for you (sort of...). In other words, prepared statements allow MySQL (or whatever RDBMS) to decide how to handle data. When putting quotes, that would force it to be a string which doesn't make sense. If it's supposed to be a string, then the server will handle that.

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

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, '%')"

Categories