"SELECT * FROM users WHERE id IN ( )" == FAIL - php

I have a function that I use called sqlf(), it emulates prepared statements. For instance I can do things like:
$sql = sqlf("SELECT * FROM Users WHERE name= :1 AND email= :2",'Big "John"','bj#example.com') ;
For various reasons, I cannot use prepared statements, but I would like to emulate them. The problem that I run into is with queries like
$sql = sqlf("SELECT * FROM Users WHERE id IN (:1)",array(1,2,3) );
My code works, but it fails with empty arrays, e.g. the following throws a mysql error:
SELECT * FROM Users WHERE id IN ();
Does anyone have any suggestions? How should I translate and empty array into sql that can be injected into an IN clause? Substituting NULL will not work.

Null is the only value that you can guarantee is not in the set. How come it is not an option? Anything else can be seen as part of the potential set, they are all values.

I would say that passing an empty array as argument for an IN() clause is an error. You have control over the syntax of the query when calling this function, so you should also be responsible for the inputs. I suggest checking for emptiness of the argument before calling the function.

Is there a possibility that you could detect empty arrays withing sqlf and change the SQL to not have the IN clause?
Alteratively, you could postprocess the SQL before passing it to the "real" SQL executor so that "IN ()" sections are removed although you'd have to do all sorts of trickery to see what other elements had to be removed so that:
SELECT * FROM Users WHERE id IN ();
SELECT * FROM Users WHERE a = 7 AND id IN ();
SELECT * FROM Users WHERE id IN () OR a = 9;
would become:
SELECT * FROM Users;
SELECT * FROM Users WHERE a = 7;
SELECT * FROM Users WHERE a = 9;
That could get tricky depending on the complexity of your SQL - you'd basically need a full SQL language interpreter.

If your prepare-like function simply replaces :1 with the equivalent argument, you might try having your query contain something like (':1'), so that if :1 is empty, it resolves to (''), which will not cause a parse error (however it may cause undesirable behavior, if that field can have blank values -- although if it's an int, this isn't a problem). It's not a very clean solution, however, and you're better off detecting whether the array is empty and simply using an alternate version of the query that lacks the "IN (:1)" component. (If that's the only logic in the WHERE clause, then presumably you don't want to select everything, so you would simply not execute the query.)

I would use zero, assuming your "id" column is a pseudokey that is assigned numbers automatically.
As far as I know, automatic key generators in most brands of database begin at 1. This is a convention, not a requirement (auto-numbered fields are not defined in standard SQL). But this convention is common enough that you can probably rely on it.
Since zero probably never appears in your "id" column, you can use this value in the IN() predicate when your input array is empty, and it'll never match.

The only way I can think to do it would be to make your sqlf() function scan to see if a particular substitution comes soon after an "IN (" and then if the passed variable is an empty array, put in something which you know for certain won't be in that column: "m,znmzcb~~1", for example. It's a hack, for sure but it would work.
If you wanted to take it even further, could you change your function so that there are different types of substitutions? It looks like your function scans for a colon followed by a number. Why not add another type, like an # followed by a number, which will be smart to empty arrays (this saves you from having to scan and guess if the variable is supposed to be an array).

Related

$wpdb->prepare placeholders %d %s , working, but I am not convinced I have done it the best

I have managed after a struggle to understand what is happening with the prepare placeholders. My only thought is that my table does not have a consistent element in it that I can use as a reference with the place holder.
There is a test column that I have used, but i do not intend on having it in my production plugin. The column is set to 0 for each entry, and I set the $test to 0. Thus my query has now started working. But this doesn't really make sense as a security feature unless it is dynamically calling something in reference to the results on the database. The examples I have seen around all rely on a set constant in their query, but I haven't got this unless I just add a constant entry in the database, but this seems silly.
$test = 0;
$result =
$wpdb->get_results( $wpdb->prepare
( "SELECT * FROM $my_noted_table_name WHERE id_can_view = %d ", $test));
Is there a better way of doing this?
Thanks in advance..
Let me explain what is happening.
The prepare is sanitizing the variable's value, inserting it where you specified the placeholder, and then formatting the SQL query. Then the returned SQL query string is processed by the $wpdb->get_results().
Step 1:
For this line of code:
$wpdb->prepare( "SELECT * FROM $my_noted_table_name WHERE id_can_view = %d", $test );
here's what is happening:
Sanitizes the variable's value $test
Replaces out the placeholder with the sanitized variable's value.
The database table name is extracted from your $my_noted_table_name variable.
Formats the SQL query
For the placeholder, %d means the value will be an integer. If it's a string, then use %s instead. Think about it in terms of using the PHP construct sprintf or printf.
d - the argument is treated as an integer, and presented as a (signed) decimal number.
s - the argument is treated as and presented as a string.
So, let's say your variable $test has a value of 100 assigned to it and the database table's name is countries. Then SQL query string then is:
"SELECT * FROM `countries` WHERE `id_can_view` = 100;"
See how $wpdb->prepare transformed your inputted string into a properly formatted SQL query?
You want to ALWAYS use $wpdb->prepare() to handle this process as it will protect your database.

How to use wildcard in PHP query

I have a table filter feature in PHP club membership webpage. I made it so the user can filter the table and choose which members to display in a table. For example, he can choose the country or state where the member is from then hit display. I am using a prepared statement.
The problem is, I need to use wildcards to make the coding easier. How do I use a wildcard in PHP MySQL query? I will use wildcards for example if the user does NOT want specific country but instead he wants to display all members from all countries.
I know not specifying the WHERE country= will automatically select any countries but I already constructed it so each controls like the SELECT control for country already has a value like "CA" or "NY" and "*" if the user leaves that control under "All Countries". This value when submitted is then added to the query like:
$SelectedCountry = $_POST["country"];
sql .= " WHERE country=" . $SelectedCountry;
But the problem is using WHERE country=* doesn't seem to work. No errors, just doesn't work. Is "*" the wildcard in PHP MySQL?
The * is not a wildcard in SQL when comparing with the = operator. You can use the like operator and pass a % to allow for anything.
When doing this the % should be the only thing going to the bind. $Bind_country = "'%'"; is incorrect because the driver is already going to quote the value and escape the quotes. So your query would come out as:
WHERE country ='\'%\''
The = also needs to be a like. So you want
$bind_country = '%';
and then the query should be:
$sql = 'select * from table where country like ?';
If this were my application I would build the where part dynamically.
Using * in WHERE clause is not right. You can only give legit value. For example:
// looking for an exact value
SELECT * FROM table WHERE column = 'value'
// you can also do this when looking for an exact value
// it works even if your $_POST[] has no value
SELECT * FROM table WHERE column = 'value' OR '$_POST["country"]' = ''
// looking for a specific or not exact value
// you can place % anywhere in value's place
// % denotes the unknown characters of the value
// it works also even if your $_POST[] has no value
// results will not be the same when you're using AND or OR clause
SELECT * FROM table WHERE column LIKE '%val%'
I think below link can solve your problem.
Just have a look and choose what you need.
Thanks.
http://www.w3schools.com/sql/sql_wildcards.asp

Query in PHP with 'and' or 'or' as variable

I would like to do search for advanced search. The Search feature has and/or for every category. User can choose any combination of and n or. Here i give the screenshot
I store the and/or into variable call $pil, $pil1,$pil2 and $pil3. And will put them in query. it's better than validate one by one every condition of and/or
So this is my query using postgresql in PHP
$query = pg_query("SELECT evaluationdate,onlinename,channel,topik,reviewername,sourceevaluation,evaluation
from AgentPerformance
where onlinename like '%".$VEOn1."%'
".$pil." reviewername like '%".$VERev1."%'
".$pil1." channel like '%".$VEChan1."%'
".$pil2."sourceevaluation like '%".$VESource1."%'
".$pil3."evaluationdate between '".$VEStart1."' and '".$VEEnd1."'");
EDIT :
The problem now, All the variables must not be empty or the query will be error. any way to trick this?
You've missed some spaces near sourceevaluation and evaluationdate
Try with this query :
$query = pg_query("SELECT evaluationdate,onlinename,channel,topik,reviewername,sourceevaluation,evaluation
from AgentPerformance
where onlinename like '%".$VEOn1."%'
".$pil." reviewername like '%".$VERev1."%'
".$pil1." channel like '%".$VEChan1."%'
".$pil2." sourceevaluation like '%".$VESource1."%'
".$pil3." evaluationdate between '".$VEStart1."' and '".$VEEnd1."'");
Simply. Use validation for each $pil whether it is empty or not. it makes me validate 4 times, but it solves the problem. The syntax error has been solved too

SQL-injection with binding of an array to =ANY() condition

It assumed a more complex query with multiple bindings so please don't guide me to use the things like implode(',',$ids), (?,?,?) or PDO possibilities for this example.
The question is to clarify a possibility of the SQL-injection of this specific method.
There is parameter 1,2,3 in the url http://localhost/executeSql/1,2,3.
The parameter is passed by binding into = ANY operator as the string representation of the array '{1,2,3}' of PostgreSQL 9.3.
The php-code on Laravel 5.1:
public function executeSql($ids)
{
$ids='{'.$ids.'}';
$condition = 'WHERE id = ANY(:ids)';
$sql="SELECT id FROM (VALUES (1),(2),(3)) AS t(id) $condition";
DB::select($sql,[':ids'=>$ids]);
}
The result is the query:
SELECT id FROM (VALUES (1),(2),(3)) AS t(id) WHERE id = ANY('{1,2,3}')
That's works well untill the parameter contains integers only.
If the parameter is 1,2,3+ the QueryException occurs:
Invalid text representation: 7 ERROR: invalid input syntax for integer: "3+"
Can it be considered a proper protection to avoid SQL-injection?
As far as I understand from the documentation here and here , ANY convert the string you pass into an array and then use the operator (=) to compare each value in the array for one that would match.
In this case, I think pgsql do a little more: it has seen the lvalue (id) is of type integer, so it expect an array of integers. Since 3+ is not an integer, you have this one.
You should probably inspect the content of ids array (using filter_var and like) to ensure you have only integer values.
Since you definitively want the query to run with unintended result, this fails as a proper SQL injection because ANY checks its input and the query fails before running.
If however pgsql comes with a facility to build an array of integer from range, like {1:999999999999}, then you probably have a problem because the query will match a lot whole more rows.

logic in sql statement

connect1($db_host,$db_username,$db_password,$db_name1);
$q="SELECT DISTINCT
bizinfo.dbiz_id,
bizinfo.company_name,
bizinfo.company_industry,
bizinfo.company_sub_industry
FROM
bizinfo
Inner Join biz_feedback ON bizinfo.dbiz_id = biz_feedback.biz_id AND biz_feedback.on_industry = bizinfo.company_industry
ORDER BY
bizinfo.dbiz_id ASC";
$rs_q=mysql_query($q);
while($row=mysql_fetch_assoc($rs_q))
{
$dbiz_id=$row['dbiz_id'];
$company=$row['company_name'];
$company_industry=$row['company_industry'];
$company_sub_industry=$row['company_sub_industry'];
connect2($db_host,$db_username,$db_password,$db_name2);
$sql_livedb=mysql_query("UPDATE bizinfo set bizinfo.company_industry='$company_industry', bizinfo.company_sub_industry='$company_sub_industry'
WHERE bizinfo.dbiz_id='$dbiz_id'");
}
When this code is run, all rows in the company_industry and company_sub_industry columns are filled with the same data (for the first biz_id).
Somewhere a join needs to happen, but I thought I had it covered here WHERE bizinfo.dbiz_id='$dbiz_id'.
It isn't at all obvious what your problem is, but I would guess that there is a value in one of the variables (probably $company_sub_industry) which has a single quote embedded in it, and consequently results in the WHERE clause being ignored.
Change your code to remove the update statement and instead dump the values so you can check them. Perhaps some error checking after/in mysql_query would help.
If this is the cause, the real solution is to not generate SQL by concatenating strings and variables. Research "php avoid SQL injection" for more info.

Categories