One day I was googling to understand when a prepared statment should be used. As I searched online many claimed that you should use always prepared statments. Now the question I have is... does this also count for LIMIT? I mean it is possible (I tried) but is it really that rational? Same question on ORDER BY too.
When the database does not allow you to use a parameter on a specific location of the SQL statement you need to assemble the query on the fly, by the use of Dynamic SQL. That is... concatenating strings to get a full functioning SQL query.
Now, the trick is to make it safe against SQL Injection. To do this:
Make sure the value for LIMIT is an integer, and not something unsafe coming right from the UI like 3; DROP TABLE EMPLOYEE.
For the ORDER BY clause make sure the columns are not coming from the UI "as is"; use some kind of projection. For example, if there are 50 columns to order by, the UI can display them all, but then just send a number (from 1 to 50) to the backend; the backend receives this number and reconstitutes the ordering column(s) from it.
Normally the LIMIT parameters must be literals, not values that can be substituted for placeholders, so you would have to validate that they're integers before substituting into the string.
However, if you use PDO rather than mysqli, it allows you to perform parameter substitution in the LIMIT clause, by using the PDO::ATTR_EMULATE_PREPARES option. This is automatically enabled for a specific prepared statement if it contains parameters in this clause. See How to apply bindValue method in LIMIT clause? for the specific details.
Related
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
I am converting a Coldfusion website to PHP. I have a query that looks in a list of comma separated numbers to see if there is a match and then responds accordingly. This is the where statement I am currently using.
WHERE (`link_issue_category`.`Category_ID` IN (<CFQUERYPARAM value="#Category_id#" list = "yes">)
How do I write this in PHP?
CFQUERYPARAM does some validation on the value, and if possible sets up a bind parameter. You can probably just embed the value into the SQL, assuming you've already done validation / sanitization on it. The list parameter specifies that this is a comma-delimited list. You should be able to plug this list directly into the query, depending on the value type.
"WHERE (`link_issue_category`.`Category_ID` IN ($category_id)";
If your values in the list are strings, you may need to wrap them in qoutes before they go into the query.
FYI CF just creates a new prepared statement with the number of ? being the same as the length of your list behind the scene.
So if you want the same behaviour in PHP, it wouldn't be that hard really. Just dynamically create a new prepared statement, and bind them accordingly.
PHP Prepared statements: http://php.net/manual/en/pdo.prepared-statements.php
However, you could have just use regex to validate the list of values to numeric value and comma's only, and use the variable as part of the SQL statement.
Is there any point in saving PDO prepared statements for reuse in a session?
I'm building a site that uses MySQL fulltext queries, which I'm putting together in my PHP, like
SELECT * FROM table
WHERE MATCH (title) AGAINST ($search_string IN BOOLEAN MODE) AND
MATCH (keywords) AGAINST ($keywords IN BOOLEAN MODE)
ORDER BY $order_by $asc_desc
It seems to take a lot longer to run this kind of query when I prepare and execute it with bound parameters, than when I just prepare and execute a query string with the values included. But I need to use prepared statements to prevent the risk of SQL injection.
In any session I would very likely run the same query several times, with different parameter values. Would it make sense for me to save the PDOStatement object once it's been created (for example in the session)? If so, what would be the best way to do that? Would it be good practice to save each prepared statement in an associative array as it's created, with the SQL query string as the key for each?
On further reading I found you can't use bound params for the ORDER BY and ASC / DESC part of a statement. When I replace these with fixed values the performance improves.
There is no benefits to store prepare statement into session for reusable purpose,
the expensive cost is on the query execution itself.
If the data you are grabbing constantly changes, then caching the resultset may no longer be valid shortly after you retrieve them.
However, if your data appears to be static:
session_start();
$_SESSION['cachedResultSet'] = $fetchedResultset;
echo $_SESSION['cachedResultSet'][0][0];
# above statement `echo ...` depends on how your result is, be it an array or object
I think it would be acceptable to a degree to store resultsets in session variables, but perhaps it would depend (sorry to be redundant) whether the data changes quickly or on how often you grab this particular resultset, etc...
Should I use a placeholder for every parameter even if it a parameter is always same? And how about nulls? Should I use placeholders for them also?
I'm interested in avoiding preparing and enable caching of the query. I'm using mysql, php and pdo but I think this is the same also with other databases. Security is not a deal because the parameters in the question are hard coded.
Case 1: Should I use a placeholder for visibility or is hard coded value 1 better?
select * from table where visibility=1 and product=:id
Case 2: Should I use a placeholder for null? (and is it the same for both cases?)
select * from t1 where color is null
update t1 set color=null where product=:id
if parameters are hardcoded, there is no use for the placeholders at all.
Query caching is enabled by mysql by default, so, you have no worry about it.
So, if there are really no dynamically changed parameters, there is no use in preparing and executing. Just use PDO query method.
However, the question from your title, Which parameters should I declare as sql placeholders? is muhch more interesting.
in terms of that clumsy PDO library, you have to use placeholders for data only.
but in terms of creating SQL queries dynamically, you have to use appropriate placeholders for every variable part of query.
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.