3

OK, so I have gone round and round with this now for 2 hours and cannot figure out where the so-called SQL syntax error is. I finally re-wrote the prepared statement as a standard query - and it works fine, literally identical syntax.

Prepared Statement Code: (NOT working)

if ($account_info = $mysqli->prepare("SELECT users.specid, users.username ?
    FROM users ? WHERE users.id = ?")) {
     //A SWITCH to determine bind_param and bind_result
} else {
     //Error output
}

The above results in the following MYSQL error:

You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '? FROM users ? WHERE users.id = ?' at line 1

Now if I literally change the '?' to $variables and make the prepared statement into a normal query like:

if ($account_info = $mysqli->query("SELECT users.specid, users.username $param1 
    FROM users $param2 WHERE users.id = $param3")) {
     //Fetch array and set variables to results
} else {
     //Error output
}

The above code WORKS as expected with no errors.

For those curious what the $variables are in the specific switch case I'm testing:

$param1 = ', tenants.paper';
$param2 = ', tenants';
$param3 = $_SESSION['user_id'].' AND tenants.id = users.specid';

So why does one work but not the other when they have the same syntax??? It doesn't even get to the bind_param part!? I'd prefer to use the prepared statement method.

Dylan
  • 45
  • 5
  • I'm just curious, why do you include the comma as a value of a variable rather that including it directly to the query? – Carl Binalla Jul 13 '17 at 06:06
  • generally you should not use table & column names in prepared statements – Simhachalam Gulla Jul 13 '17 at 06:07
  • 1
    You can only bind **values** not columns and not aliases if you are getting columns or aliases from user input you're doing something you shouldn't be doing. – apokryfos Jul 13 '17 at 06:10
  • @Swellar I'm not including the comma because one case requires no additional parameters in the statement. – Dylan Jul 13 '17 at 06:17
  • @SimhachalamGulla I have to, I need to select from 2 different tables and intersect those tables. – Dylan Jul 13 '17 at 06:18
  • @Dylan Have you tried creating two different query instead? – Carl Binalla Jul 13 '17 at 06:20
  • @apokryfos I didn't know that... and that would present a big problem and render a prepared statement useless here. Although I don't think that's why it's failing because it doesn't even get to that point? And if it matters, it's not user input, it's account type (a database value) that's determining what needs to be pulled. – Dylan Jul 13 '17 at 06:21
  • @Swellar I was really trying to avoid that, takes a lot more code then as the entire thing would have to be written 3 times with small differences. Seems like there ought to be a better way. – Dylan Jul 13 '17 at 06:24
  • @Dylan if it doesn't get to that point then the problem is not in the code you've shared. Also, column names don't need to be parametrised to be escaped, a simple wrapping in backticks and removing backticks within them is enough. – apokryfos Jul 13 '17 at 06:25
  • Possible duplicate of [Can PHP PDO Statements accept the table or column name as parameter?](https://stackoverflow.com/questions/182287/can-php-pdo-statements-accept-the-table-or-column-name-as-parameter) - edit: noticed you're using mysqli, and this is pdo - but the answer is the same in any case. https://stackoverflow.com/questions/11312737/can-i-parameterize-the-table-name-in-a-prepared-statement – Qirel Jul 13 '17 at 06:36
  • @apokryfos After reading the below answers... it actually is in the code above. A prepared statement with a variable where a table name and/or column name should be will fail. So, the first and second '?' will cause it to fail. And after testing removing them, I do in fact get it to move past to bind_param. Not sure what you mean by the rest of that though? Can you give an example? – Dylan Jul 13 '17 at 06:36

3 Answers3

2

You can't pass object nane (tablename or columnname ) as param .

So users.username ? and users ? as you are trying to use are wrong ..

passing param is not a string substituition ..

This kind of action are disallowed by param binding
and you should avoid this ..but if you really need then try with string concatenation

ScaisEdge
  • 131,976
  • 10
  • 91
  • 107
0

You only bind values for parameter bindings. Not parts of SQL. ::bind_param

What you are trying to do with $param1 = ', tenants.paper'; is already SQL injection. Prepared statements are build to prevent this.

You should make a method per query instead of a generic query.

fabpico
  • 2,628
  • 4
  • 26
  • 43
  • Thanks, same thing scaisEdge said, that makes sense. Didn't want to have to build 3 slightly different queries. – Dylan Jul 13 '17 at 06:38
  • You should. Otherwise you make your queries highly vulnerable to errors and injection. If you want to build a generic query i suggest you to use a builder like [DBAL QueryBuilder](http://docs.doctrine-project.org/projects/doctrine-dbal/en/latest/reference/query-builder.html) – fabpico Jul 13 '17 at 07:42
0

You cannot bind complex query parts and columns in a query. I also don't understand why you need to parametrise strings you explicitly set in your code.

Do this instead:

$param = $_SESSION['user_id'];
if ($account_info = $mysqli->prepare("SELECT users.specid, users.username, tenants.paper
    FROM users JOIN tenants ON tenants.id=users.specid WHERE users.id = ?")) {
     //A SWITCH to determine bind_param and bind_result
} else {
     //Error output
}

If you (at any point in the future) need to escape column names from user input (though you shouldn't allow users such power to begin with) do this:

 $columnNameFromUserInput = $_GET["column"];
 $columnNameFromUserInput = "`".str_replace("`","",$columnNameFromUserInput)."`";

This should be enough.

Do not put query segments that have parts that need escaping in a variable. Put the parts that need escaping in their own separate variables so you can bind them is the whole idea here.

Example:

$param1 = ', tenants.paper'; //Bad has a comma in it, should be `tenants`.`paper` and the comma should go in the query itself
$param2 = ', tenants'; //Bad, though you have to use JOIN in any SQL language after 1992 
//The next part is very very bad.
// You have something that needs escaping mixed with things that compose a query. Split them. 
$param3 = $_SESSION['user_id'].' AND tenants.id = users.specid'; 
apokryfos
  • 38,771
  • 9
  • 70
  • 114
  • I was trying to use one prepared statement to perform 3 different queries, each requiring pulling from 1 different table (1 the same in all). Absolutely ZERO user input is directly passed anywhere here. (the $_SESSION variable is actually set by the login script when the user logs in, and is used in many places). In fact, a similar variable is used to determine which query to run. Segments were combined in the process of troubleshooting.... Although I didn't realize how bad doing it that way was. Also, didn't think of using JOIN - I will do that! – Dylan Jul 13 '17 at 16:29