I have a database table with a few BIT(1) type columns. If I were to forget about prepared statements, I could easily do something like this:
UPDATE tablename SET
bit_column_1 = b'1',
bit_column_2 = b'0'
And this would work perfectly. However, no matter what I try, when using prepared statements, the value is always '1'.
I have done the following and none of them work if $_POST['bit_col'] is 0:
$stmt = $dbh->prepare("UPDATE tablename SET
bit_col = :bit_col ");
// First attempt
$stmt->bindValue('bit_col', $_POST['bit_col']);
// Second attempt
$stmt->bindValue('bit_col', $_POST['bit_col'], PDO::PARAM_INT);
// Third attempt
$stmt->bindValue('bit_col', "b'{$_POST['bit_col']}'");
Then I tried altering the prepared statement to put the b there, but I get number of bound variables does not match number of tokens
$stmt = $dbh->prepare("UPDATE tablename SET
bit_col = b:bit_col ");
$stmt->bindValue('bit_col', $_POST['bit_col']);
$stmt = $dbh->prepare("UPDATE tablename SET
bit_col = b':bit_col' ");
$stmt->bindValue('bit_col', $_POST['bit_col']);
Another thing worth mentioning is PDO::ATTR_EMULATE_PREPARES is set to true. Setting this to false would require me to refactor things quite a bit because of how I unknowingly managed database connections.
So my question is, is it possible to use prepared statements with BIT columns in MySQL, and if so, how?
PDO::ATTR_EMULATE_PREPARES causes PDO to interact with BIT columns slightly differently. If it is set to false, you just insert the value as normal and MySQL will do any conversion necessary behind the scenes:
$stmt = $dbh->prepare("UPDATE tablename SET
bit_col = ? ");
$stmt->bindValue(1, $_POST['bit_col']);
However, if PDO is emulating the prepared statements, you need to put a b in there somehow to indicate that it is a BIT type. If you put the b in the bound parameter, PDO will escape the single quotes and you will end up with something like 'b\'0\'' being sent to MySQL, which obviously won't work. The b therefore needs to be in the query, not in the bound parameter. Doing this with named parameters produces the "number of bound variables does not match number of tokens" error above because PDO does not recognize strings with a b followed by a : as a named parameter. However PDO does recognize it as a parameter when you use question mark parameter markers, like this:
$stmt = $dbh->prepare("UPDATE tablename SET
bit_col = b? ");
$stmt->bindValue(1, $_POST['bit_col']);
Since we need to end up with something like b'1' being sent to MySQL, using PDO::PARAM_INT when binding the value will cause the query to fail because it will become UPDATE tablename SET bit_col = b1 (without the quotes around the number) so you must leave the data type out or use PDO::PARAM_STR.
Also note that if emulating prepared statements is disabled, this query will fail with a syntax error, so unfortunately the queries need to be done completely differently depending on whether or not you are emulating prepares or not.
if I'm not mistaken, the syntax should be:
$stmt->bindValue(':bit_col', $_POST['bit_col']);
to reference the :bit_col from:
$stmt = $dbh->prepare("UPDATE tablename SET bit_col = :bit_col ");
Related
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
From experience and also having been told constantly the benefits of using prepared statements and binding my parameters, I have constantly used those two techniques in my code, however I would like to understand exactly the purpose of each of those two techiques:
From my understanding of prepared statements:
$sql = "SELECT * FROM myTable WHERE id = ".$id;
$stmt = $conn->prepare($sql);
$stmt->execute();
The previous code should create a sort of a buffer in the database with the query I proposed. Now FROM MY UNDERSTANDING (and I could be very wrong), the previous code is insecure, because the string $sql could be anything depending on what $id actually is, and if $id = 1; DROP TABLE myTable;--, I would be inserting a malicious query even though I have a prepared statement.
FROM MY UNDERSTANDING this is where binding my parameters com in. If I do the following instead:
$sql = "SELECT * FROM myTable WHERE id = :id";
$stmt = $conn->prepare($sql);
$stmt->bindParam(':id', $id);
$stmt->execute();
The database should know exactly all the parts of the sql statement before hand:
SELECT these columns: * FROM myTable and WHERE id = "a variable that was input by the user", and if "a variable that was input by the user" != a variable, the query fails.
I have been told by some my understanding is correct, and by others that it is false, could someone please let me know if I am wrong, correct, or missing something? And elaborate as much as you want, all feedback is greatly appreciated!
You're correct that the first case is insecure. It's important to understand though, that preparing a statement only has value if you are using variable data, and/or executing the same query repeatedly. If you are executing plain statements with no variables, you could simply do this:
$sql = "SELECT * from myTable WHERE this_column IS NOT NULL";
$result = $conn->query($sql);
And end up with a PDOStatement object to work with, just like when you use PDO::exec().
For your second case, again, you're largely correct. What's happening is the variable passed to the database is escaped and quoted (unless you specify otherwise with the third argument to PDOStatement::bindParam(), it's sent as a string which is fine for most cases.) So, the query won't "fail" if bad data is sent. It behaves exactly as if you had passed a valid number that didn't exist as an ID in the database. There are, of course, some edge cases where you are still vulnerable even with a correctly prepared statement.
Also, to make life easier, you can use prepared statements like this, to do implicit binding:
$sql = "SELECT * FROM myTable WHERE id = :id";
$stmt = $conn->prepare($sql);
$stmt->execute([":id"=>$id]);
Or even like this, with un-named parameters:
$sql = "SELECT * FROM myTable WHERE id = ?";
$stmt = $conn->prepare($sql);
$stmt->execute([$id]);
Naturally, most of this has been explained in the comments while I was typing up the answer!
I've read that the PDO::Prepare function creates a safe query. Does this mean escape characters don't need to be manually literalised? Such as the backslash character.
No it absolutely does not mean that. What you read is misleading.
There is a difference between a "prepared statement" and a "parameterized query." You want the latter for sanitation purposes.
For example:
$pdo->prepare("SELECT * FROM t1 WHERE col1 = $USER_PROVIDED_VALUE");
is not safe at all even though it is prepared. Instead, you have to do this:
$stmt = $pdo->prepare("SELECT * FROM t1 WHERE col1 = ?");
$stmt->execute(array($USER_PROVIDED_VALUE));
Preparing the query isn't going to do anything for you in terms of security if you do not properly parameterize it.
my goal here is to be able to get a variable (with php) and use it in a prepared statement (with mysqli), and then fetch_assoc. For some reason this code will not work (no errors). I've rtm and I haven't found anything combining fetch_assoc with prepared statements, so I'm not sure if it's even possible. Any help to get this working is appreciated, here's my code currently.
$where = $_GET['section'];
$mysqli = mysqli_connect("localhost", "root", "","test");
if($stmt = mysqli_prepare($mysqli,"SELECT title, img, active, price FROM ? ORDER by ID limit 5 ")){
mysqli_stmt_bind_param($stmt, 's', $where);
mysqli_stmt_execute($mysqli);
mysqli_stmt_fetch($mysqli);
while($row = mysqli_fetch_assoc($stmt)){
if($row['active']=="yes"){
echo 'the rest of my stuff goes here';
From the PHP website page for mysqli->prepare (with emphasis added to the most relevant part):
Note:
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.
However, they are not allowed for identifiers (such as table or column
names), in the select list that names the columns to be returned by a
SELECT statement), or to specify both operands of a binary operator
such as the = equal sign. The latter restriction is necessary because
it would be impossible to determine the parameter type. In general,
parameters are legal only in Data Manipulation Language (DML)
statements, and not in Data Definition Language (DDL) statements.
Assuming you can get past that problem, your use of mysqli is a little confused. You correctly bind your parameters and execute, but you've mixed up two different ways of getting at your results. Either
Use mysqli_stmt_get_result to fetch the result set and then use mysqli_fetch_assoc on that, or
Bind your results with mysqli_stmt_bind_result, and then use mysqli_stmt_fetch to fetch the next set of results into your bound variables. (Usually you'd iterate over the results using something like while(mysqli_stmt_fetch($stmt)){ //do stuff here }
Another way style, we can write it below:
$mysqli=new mysqli("host","user","pass","db");
$stmt = $mysqli->prepare($query);
$stmt->bind_param('s', $variable);
$stmt->execute();
$result = $stmt->get_result();
while($row = $result->fetch_assoc()){
....
}
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.