Binding values and Parameters in PDO Prepared Statements :PHP - php

As PDOStatement::execute documentation says, "All values are treated as PDO::PARAM_STR.
So I have following questions
1)Suppose I have a variable $_SESSION['id']=2 and a query
$sql='select * from articles where id=?';
$query=$con->prepare($sql);
$query->execute(array($_SESSION['id']));
When I execute the statements,it executes successfully.
It signifies that it passes variable as an integer .Does it do automatic casting or it violates the documentation statement?
2)Suppse I have another query
select * from articles where id=? and category=?
Now here first parameter is int and last parameter is string so when I bind parameters $query->bindValue(2, $_GET['category'],PDO::PARAM_STR); do I need to specify PDO::PARAM_STR or I can rely on the default implementation that it is considered as string by default as the documentation says.
3)I want to specify table name from $_GET['category'] into the query but I get the following format of the string(notice the quotes around table name) and hence SQL error.How can I correct it?
select * from 'article'...........

1) When I execute the statements,it executes successfully. It
signifies that it passes variable as an integer .Does it do automatic
casting or it violates the documentation statement?
No, it does pass the parameter as string. MySQL is casting it back to an int transparently because the column is an int value, which in this case has no side effects.
2) do I need to specify PDO::PARAM_STR or I can rely on the default
implementation that it is considered as string by default as the
documentation says.
Unless the API ever changes, it's pretty sure that it's bound as a string if the documentation says so. I doubt the API will change that any time soon, or at all. I might still explicitly bind it as string, just to make it extremely clear to a reader of the source code.
3) I want to specify table name from $_GET['category'] into the query
...
You cannot. You can only "placehold" values, not identifiers or other structural elements of the query. Parameterised statements are explicitly to separate between the structure of the query and the values dynamically interpolated into it; if you could dynamically interpolate structural elements there'd be no point to this separation.

Related

What is the reasoning for why table names can not be susbstituted in PDO prepared statements?

This works as expected:
$st = $db->prepare('SELECT COUNT(1) WHERE EXISTS (SELECT * FROM ' . $table_name . ')');
$st->execute(array());
This throws a syntax error:
$st = $db->prepare('SELECT COUNT(1) WHERE EXISTS (SELECT * FROM :tblname )');
$st->execute(array('tblname' => $table_name));
As per the comments, "data can only be bound when it can be represented as a numeric or a quoted string literal. A table name can neither be a numeric or string literal."
What is the reasoning behind this?
Parameters are not just a string substitution. That's actually what makes them good at preventing SQL injection vulnerabilities.
(Well, in some clients, they emulate query parameters using string substitution. PDO has this behavior if you set the PDO::ATTR_EMULATE_PREPARES attribute. But I'll talk about true parameters.)
The reason that parameters are not string substitution is that they are combined with the SQL query after it has been parsed by the database server. The parsing step validates your SQL syntax is correct, and validates that the tables and columns you have named in the query in fact exist.
The only thing that can be omitted from the parsing step are constant values in expressions. The parser sees one of the parameter placeholders and knows that that token acts as if you had used a single scalar constant value. Therefore it can proceed with the syntax validation.
The following example is not valid SQL syntax because you can't SELECT from a constant string:
SELECT COUNT(1) WHERE EXISTS (SELECT * FROM 'mytable' )
But that's how the syntax will be interpreted if the table name is passed as a parameter. The SQL engine doesn't know the exact value of the parameter, but it knows it should be treated as a string value.
This way, the query can be parsed once (during the prepare() call), and then once parsed, you can execute the prepared query multiple times, plugging in different values for each execution (the execute() call). Those parameter values cannot change the logic of the query, only the constant values.
And that's the benefit for protecting against SQL injection. SQL injection occurs when dynamic content is combined with a query before it is parsed, so the content can change the syntax and therefore the logic of the query, making it do something you didn't intend. But if parameters are combined after parsing, they can't affect the logic.
In SQL, table, column, and schema names cannot be provided as parameters to prepared statements. It's not a PDO thing, it's a language restriction of SQL itself.
Many RDBMSs have semantics for preparing statements once and then using the prepared statements multiple times with different data. This is efficient because the RDBMS software works out the query plan – the operations it must do to satisfy the query – just once, and then reuses it with different data. If table or column names were variables in prepared statements the RDBMS software could not do that. PDO supports multiple RDBMSs so it enforces this language feature.
Prepared statements are also a security feature. When you provide parameters and use the statement, the data types of parameters get checked carefully. That would be much harder if table or column names could be parameters.
And, it's the way SQL was defined a generation ago. Data in RDBMSs lasts for decades.

Do I need to use prepared statement for the data I'm getting from database?

I have a query like this:
$result = $db
-> prepare("SELECT value FROM mytable WHERE id = ?")
-> execute(array($id))
-> fetch(PDO::FETCH_ASSOC);
And I want to use $result['value'] as a parameter for another query (an UPDATE statement). Should I use prepared statement for that UPDATE statement? Or because I've taken it from database, then no need to pass it as prepared statement?
Yes. Use a prepared statement with a bind placeholder.
Just because a value is being returned from a database doesn't mean that the value is safe for inclusion in SQL text.
You may have domain knowledge that the value column in mytable is INTEGER type, so that it would be safe. But in the more general case, and for the reader who doesn't know the definition of mytable, and what value might contain. A reader of your code is going to assume that value isn't "safe". For all we know, we could be getting something like this:
Robert'); DROP TABLE students; --
Whenever we see a variable concatenated into the SQL text, we are going to assume that the variable could contain something other than a value, and it could contain actual SQL. (Or, if we do see a variable concatenated into the text of a SQL statement, we would be expecting that it's going to be properly escaped right at the point it's being concatenated.)
So, the preferred pattern would be use a prepared statement with a bind placeholder. That makes it unambiguous to the reader that value is indeed a value, and that it's not intended to be interpreted as SQL text.

specifying mysqli bind_param types

what are the pros and cons of specifying mysqli bind_param types i,d,s,b?
I store a UNIX timestamp in a db column with data type INT. In my prepared statement I set it to bind_param('s',$timestamp); and it stores the value without any problems. So what impact did it make that I used s instead of i during bind_param?
It influences how MySQL will see the query/value. Take this query:
INSERT INTO foo VALUES (?)
If you bind the parameter as int:
$stmt->bind_param('i', $bar);
Then the query will be about equivalent to:
INSERT INTO foo VALUES (42)
However, if you bind as string:
$stmt->bind_param('s', $bar);
The query will be about equivalent to:
INSERT INTO foo VALUES ('42')
In many cases MySQL doesn't particularly care whether you send it a string or an int and will simply cast the value to the appropriate type on the fly, just as PHP does most of the time. However, in some circumstances and with some types you may get an unexpected conversion, which is why you should bind the value as the type that you want it as and not leave it up to MySQL's casting rules.

Should we always bind our SQL statements?

I have been researching into PDO's bindValue(). I know that preparing my SQL statements with PDO is keeping SQL injections from happening.
Code Example:
$stmt = $dbh->prepare('SELECT * FROM articles WHERE id = :id AND title = :title');
$stmt->bindValue(':id', PDO::PARAM_INT);
$stmt->bindValue(':title', PDO::PARAM_STR);
$stmt->execute();
By binding the ID as a number, and the Title was a string, we can limit the damage done when someone tries to do an SQL injection within the code.
Should we always bind our values with a PDO::PARAM_ so we can limit what can be pulled from the database in an SQL injection? Does this add more security with PDO when doing our bindValue()?
There are two questions in one. It is essential not to confuse them
Should we always use a placeholder to represent a variable data in the query?
Should we always use certain function in the application code to follow the above rule?
Also, from the clarification in the comments under the opening post, the third question can be seen:
Should we always use third parameter, or it's OK to let PDO bind all the parameters as strings by default?
1. For the first question the answer is absolutely and definitely - YES.
While for the second one, for sake of code sanity and DRYness -
2. Avoid manual binding when possible.
There are many ways to avoid manual binding. Some of them are:
ORM is an excellent solution for the simple CRUD operations and must have in a modern app. It will hide SQL from you completely, doing the binding behind the scenes:
$user = User::model()->findByPk($id);
Query Builder is also the way to go, disguising SQL in some PHP operators but again hiding the binding behind the scenes:
$user = $db->select('*')->from('users')->where('id = ?', $id)->fetch();
some abstraction library may take care of the passed data by means of type-hinted-placeholders, hiding the actual binding again:
$user = $db->getRow("SELECT * FROM users WHERE id =?i", $id);
if you are still using PHP in the last century ways, and have raw PDO all over the code - then you can pass your variables in execute(), still saving yourself a lot of typing:
$stmt = $dbh->prepare('SELECT * FROM users WHERE id = ?');
$stmt->execute([$id]);
$user = $stmt->fetch();
As of the third question - as long as you are binding numbers as strings (but not the opposite!) -
3. It's all right with mysql, to send almost every parameter as a string
as mysql will always convert your data to the proper type. The only case known to me, is a LIMIT clause where you cannot format number as a string - thus, the only related case is one when PDO is set in emulation mode and you have to pass a parameter in LIMIT clause. In all other cases you can omit third parameter, as well as explicit call to bindValue() without any problem.
You should definitely use the prepare API and pass values separately from the query, as opposed to doing plain string interpolation (e.g. "SELECT * FROM foo WHERE bar = '$baz'" → bad).
For binding parameters, you have three options:
bindParam
bindValue
execute
It doesn't really matter which of these you use, they're all equally secure. See these answers for some details about the differences:
Confusion between bindValue() and bindParam()?
Using PDO without binding
When using bindParam or bindValue, passing the third PDO::PARAM_ argument type is optional. If you don't pass it, it defaults to binding the argument as string. That means you may end up with a query equivalent to ... WHERE foo = '42' instead of ... WHERE foo = 42. It depends on your database how it will handle this. MySQL will cast the string to a number automatically as needed, just as PHP does (e.g. in '42' + 1). Other databases may be more fussy about types.
Again, all options are equally safe. If you're trying to bind a string 'foo' using PDO::PARAM_INT, the string will be cast to an integer and accordingly bound as the value 0. There's no possibility for injection.
Yes you should always bind params with prepared statement.
It's more secure, and limit SQL injection. But this is not the only think you must do to query params: a correct type control is required, best if you map a row into an object and throw an exception in it if it has invalid data.
I hope I can be useful!
Yes, binding is the way to go. Or parameterised queries which a more generalized term.
#Theo does a wonderful job explaining why parameterized queries is the way to go
You could also use stored procedures for additional security but is an over kill if you have one application to one database. Its good for multiple applications to one database to ensure consistency when handling data

Are PHP MySQLi prepared queries with bound parameters secure?

Historically, I've always used
mysql_real_escape_string()
for all input derived from users that ends up touching the database.
Now that I've completely converted over to MySQLi and I'm using prepared queries with bound parameters, have I effectively eliminated the possibility of SQL injection attacks?
Am I correct in saying I no longer need
mysql_real_escape_string()?
This is my understanding and the basis of a project of mine:
http://sourceforge.net/projects/mysqldoneright/files/Base/MysqlDoneRight-0.23.tar.gz/download
This is not something I want to get wrong though as now that I've released it, it could affect others as well.
All user provided input will now end up in bind_parms.
The queries provided in the prepare phase are static.
Yes. Using the prepared query will escape parameters.
It's not so simple. You can use bound parameters instead of interpolating application variables into SQL expressions in place of literal values only:
$sql = "SELECT * FROM MyTable WHERE id = ".$_GET["id"]; // not safe
$sql = "SELECT * FROM MyTable WHERE id = ?"; // safe
But what if you need to make part of the query dynamic besides a literal value?
$sql = "SELECT * FROM MyTable ORDER BY ".$_GET["sortcolumn"]; // not safe
$sql = "SELECT * FROM MyTable ORDER BY ?"; // doesn't work!
The parameter will always be interpreted as a value, not a column identifier. You can run a query with ORDER BY 'score', which is different from ORDER BY score, and using a parameter will be interpreted as the former -- a constant string 'score', not the value in the column named score.
So there are lots of cases where you have to use dynamic SQL and interpolate application variables into the query to get the results you want. In those cases, query parameters can't help you. You still have to be vigilant and code defensively to prevent SQL injection flaws.
No framework or data-access library can do this work for you. You can always construct a SQL query string that contains a SQL injection flaw, and you do this before the data-access library sees the SQL query. So how is it supposed to know what's intentional and what's a flaw?
Here are the methods to achieve secure SQL queries:
Filter input. Trace any variable data that gets inserted into your SQL queries. Use input filters to strip out illegal characters. For instance, if you expect an integer, make sure the input is constrained to be an integer.
Escape output. Output in this context can be the SQL query which you send to the database server. You know you can use SQL query parameters for values, but what about a column name? You need an escaping/quoting function for identifiers, just like the old mysql_real_escape_string() is for string values.
Code reviews. Get someone to be a second pair of eyes and go over your SQL code, to help you spot places where you neglected to use the above two techniques.
When you bind parameters to a prepared statement, it escapes the data automatically, so you shouldn't escape it before you send it through. Double escaping is usually a bad thing. At the very least, it produces ugly results with extra escaped characters later on.

Categories