Safe way to write a PDO query - php

Which of these two is the safe method to write a query?
$stmt = $pdo->prepare("UPDATE tableName SET fieldName = 0");
OR
$stmt = $pdo->prepare("UPDATE tableName SET fieldName = :parameter");
$stmt-> bindValue(':parameter', 0);
I know the 2nd method is way best and I use it whenever I use a $variable in bindValue. But here, I need to use a known integer 0. So, the first process seemed easier as I did not had to write another bindValue statement. But, is it safe?

Looking at your questions I'd say that you'll definitely benefit from reading the PDO tutorial I wrote, which says:
There are two ways to run a query in PDO. If no variables are going to be used in the query, you can use the PDO::query() method.
and
if at least one variable is going to be used in the query, you have to substitute it with a placeholder, then prepare your query, and then execute it, passing variables separately.
So now you can tell that for this particular query you can use the query() method instead of prepare/execute
$stmt = $pdo->query("UPDATE tableName SET fieldName = 0");
as there is no variables to be used and this no danger at all

Related

MySQLi prepared statement fails where identical regular query succeeds

I have a conventional query that works just fine that looks like this:
$result = $mysqli->query("SELECT value FROM activities WHERE name = 'Drywall'");
This succeeds in returning a row. However, for the purposes of diagnosing the problem I'm having with a prepared statement, I tried an identical query as a prepared statement like so:
$stmt = $mysqli->prepare("SELECT value FROM activities WHERE name = 'Drywall'");
$stmt->execute();
Despite the fact these are identical query strings, $stmt->num_rows is always 0. Why would the conventional query work, but the prepared statement not when they are the same exact query? Also, I realize including 'Drywall' in the prepared query string runs counter to the purpose of prepared statements, but I was just trying to eliminate the possibility that bind_param() was the culprit. So I was using bind_param() to fill in placeholders and that wasn't working either, despite my double-checking at runtime that the variable I was binding contained the correct value.
I think you want to use
$stmt->store_result();
before the call
$stmt->num_rows();
see last line of the descripton in the manual for $stmt->num_rows() (http://www.php.net/manual/en/mysqli-stmt.num-rows.php).
Check for proper use of the mysqli->prepare. The function depends on a parameter to be passed. It is different from passing the values ​​directly in the query but can use with another way.
Verify the manual:
http://www.php.net/manual/pt_BR/mysqli.prepare.php
Did you try something like this:
$stmt = $mysqli->prepare("SELECT value FROM activities WHERE name = 'Drywall'");
$stmt->execute();
$res = $stmt->get_result();
$row = $res->fetch_assoc();
PS:
Prepared statements are Good. I would urge you to ALWAYS consider using them.
But in this case, a simple query would be much more efficient (would incur fewer round trips) than a prepared statement.

Should I define my SQL statement statement handle using separate variables in PHP?

Are all 3 options the same (Y/N) or is one better (A/B/C)?
Option A - (1) defining the SQL string in the variable $sql and (2) define the statement handle using the "method" prepare on the variable $sql to compose the statement and (3) activating composition of the statement in step 2 using the execute function.
$sql = "SELECT * FROM table1";
$sth = $dbh->prepare($sql);
$sth->execute();
Option B - Similar to Option C because the ->query method is used directly on the db object and similar to Option A in that the sql statement is kept separate.
$sql = "SELECT * FROM table1";
$sth = $dbh->query($sql);
Option C - The statement handle is the sql query itself (no reference to any another variables and just using one method.
$sth = $dbh->query("SELECT * FROM table1");
Questions:
Do both options yield the same result?
If they yield the same result, is one approach recommended (i.e. best practice)?
Did I get the vocabulary like "variable", "method", and "function" correct?
Does Option A still query the db despite not explicitly using the ->query() method?
What are the pros/cons of including the sql stmnt in a seperate variable vs in the PDO Statement?
I would say both methods are the same about using a string variable or direct text string.
Assuming you using PDO there is difference between prepare statement and invoke a query statement.
In case A, the prepare statement will make the database create a plan so that statement can be reexecuted without reparsing the query string.
In case B the query is executed at once.
In your given example case B would run a little bit faster.
But if your statement uses arguments case A would benefit you with additional security due to placeholders been replaced by the driver.
Do both options yield the same result?
Yes.
If they yield the same result, is one approach recommended (i.e. best practice)?
Both are fine. In your particular example Option B gets the same job done with less code.
However, if you need to use parameters and/or constraints in your query (e.g. ...WHERE id = :id) you need to opt for option A to bind params using the $dbh->bindParam(':id', $id, PDO::PARAM_INT) method after your prepare your statement, e.g:
$dbh->prepare('UPDATE table SET column = :value WHERE id = :id');
$dbh->bindParam(':value', $someNewValue, PDO::PARAM_STR);
$dbh->bindParam(':id', $targetId, PDO::PARAM_INT);
$dbh->execute();
Doing it this way will also protect you from nasty things like SQL injections.
Storing the query in a separate variable is only needed if you plan to re-use that query at a later time in your code. Otherwise you might as well just type it within your prepare/query method directly.
Did I get the vocabulary like "variable", "method", and "function" correct?
Looks about right! Except: "the execute function" is a method. Functions and methods are basically the same things except when they belong to an object they are referred to as methods. In your example execute belongs to the $sth object, so it's called a method.
Does Option A still query the db despite not explicitly using the ->query() method?
Yes. The execute method executes the query that was prepared in $dbh->prepare(...). If you want to use parameters you can call ->bindParam() between your prepare and execute methods. If you don't need parameters, invoking ->query() directly is really the more convenient way to do it.

PDO bind parameters

I'm building simple query builder, and I have two questions:
Is it possible to secure mysql queries with normal functions to the similar level as it is done using ->execute(array(':param:' => ... ?
Is it possible to use many variables in one query, give them the same names (the ones after the semicolon), and then bind them one by one?
If I understand you correctly, you would like to know if it possible to replicate the functionality of bindParam with the standard mysql_* functions?
Short answer is no. Please do not use the mysql functions at all, use mysqli or PDO as these provide you with the true security when it comes to prepared statements. They can also provide much better query performance as the SQL is able to be pre-optimised for the database.
You will have to define each parameter separately (even if it is the same value). You could also pass a simple array to the execute() method call, but you do not then have the option to explicitly define the parameter types.
Within your function use some thing like this:
$name = "fred";
$statement = $pdo->prepare("SELECT id FROM contacts WHERE first_name = ? OR last_name = ?");
for ($x = 1; $x <= 2; $x++) {
$statement->bindParam($x, $name, PDO::PARAM_STR);
}
$statement->execute();

php PDO UPDATE statements: which is safer?

I'm learning PDO, and finding it tricky to make sure my statements work correctly. I have a PHP function which is updating my database by simply adding the number 1 to the total.
function add_rating($place_id,$rating_id) {
//make $db accessible inside the function
global $db;
// query v1
$sql = "UPDATE places_ratings SET ? +1 WHERE place_id=?";
$q = $db->prepare($sql);
$q->execute(array($rating_id,$place_id));
}
I tried variations of this, none of which I could get to work. I don't know if I was using question marks wrong. I was following this guide and also a previous SO question. In the end I tried a different method which worked first time, so I am tempted to re-use it as it also seems a lot simpler.
function add_rating($place_id,$rating_id) {
//make $db accessible inside the function
global $db;
// query v2
$query = "UPDATE places_ratings SET $rating_id = ($rating_id +1) WHERE place_id = $place_id";
$update = $db->query($query);
}
My question is: which statement is better/safer? And secondly, what am I doing wrong with the first version with question marks? Thanks...
In general prepared statements as in your first example are safer because they are immune to SQL injection.
Your example doesn't work because you can't specify field names using a ? parameter in a prepared statement. Even if you could your SQL still would be wrong, this would expand to
UPDATE places_ratings SET whatever +1 WHERE place_id=?
which is not valid.
If your $rating_id is generated in code and not taken from user input you could combine both approaches.
Prepared statements are not simply like copy'n'pasting variables into a piece of text. Prepared statements separate between the query logic and the values the query should work on. They're there so you're able to tell your database "You're supposed to do this", let the database understand it, then give it the values it's supposed to do that something with. The logic itself cannot be variable, it needs to be complete the first time.
Therefore, you can only use placeholders for values. Your query needs to read UPDATE ... SET FIELD = VALUE WHERE FIELD = VALUE. The FIELD parts need to be in the statement, the VALUE parts you can use placeholders for. It looks like your $rating_id variable is a variable field name. First of all, that's a bad idea. You should not make field names variable if possible. But if you have to, you cannot use prepared statement placeholders for them. Instead, you'll have to do it like this:
$rating_id = 'field_name';
$query = "UPDATE places_ratings SET `$rating_id` = `$rating_id` + 1 WHERE `place_id` = ?";
$stmt = $db->prepare($query);
$stmt->execute(array($place_id));
It's up to you to make sure $rating_id is safe and contains known, whitelisted values. Don't let the user supply the value for it in any way.
Please, go an learn what prepared statements are. And you could also use a tutorial, that does not promote bad practices and vulnerable code.
A correctly created and used prepared statement will always be more secure then concatenated query string, because prepared statements send query logic and data separately.
Also , if you are using PDO, then quite often the use of bindParam() method should be preferred over passing the values directly in the execute() method as an array. This is because, when passing values in execute(), the values are bound as PDO::PARAM_STR, even if DB column expects and integer.
P.S. Stop using global in your code !!

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);

Categories