Background
Hello, I’m developing an experimental/educational tool in PHP and MySQL. I’m new to SQL, but I want to do things the right way from the start. I’m using PDO prepared statements for all variable substitutions, and backticking everywhere possible (thus, as I understand, it won’t be portable to non-MySQL databases). Regarding my problem, I have an idea as to how to go forth, but it’s going to take me several hours to implement (I’m new even to the syntax of SQL), so meanwhile I thought I’d create a question first just in case someone can yell, “This is not the way to do it!” and save me hours of effort.
Problem
I would like to create an interface where a user would select from dropdown menus:
- a table
A, - one or more fields on that table, e.g.
A.xandA.y, - a table
B, - one or more fields on that table, e.g.
B.zandB.y,
and upon submission the code would perform an inner join, matching each field respectively, e.g. A.x = B.z, A.y = B.y, etc. and return all matched rows.
My plan is simply to generate an INNER JOIN SQL statement, looping through the fields and inserting placeholders (?), binding the respective parameters, and finally executing the statement.
Is there an easier way of doing this? Is there a better way of doing this? Will this be somehow exploitable?
Thank you very much, in advance. If no one responds by the time I finish (doubtful), I will post my solution.
Misc.
Assume that I will validate
- that the user selects an equal number of fields between
AandB, - that the fields and tables exist,
- etc.
and that the field names need not be identical: they will be matched in order. (Do point out any other details I might not be aware of!)
Eventually, the goal is for these selections to be saved in a “settings” table themselves. In effect, users create “views” they would like to see each time they come back.
You’re doing so much right that I actually feel guilty pointing out that you’re doing something wrong! 🙂
You can only use prepared statements to parameterise field values—not SQL identifiers such as column or table names. Therefore you won’t be able to pass
A.x,B.zetc. into yourJOINcriteria by way of prepared statement parameters: you must instead do what feels terribly wrong and directly concatenate them into your SQL string.However, all is not lost. In some vague order of preference, you can:
Present the user with an option list, from which you subsequently reassemble the SQL:
Then your form handler:
This approach has the advantage that, short of compromising PHP (in which case you’ll have far bigger concerns than SQL injection), arbitrary SQL absolutely cannot find its way into your RDBMS.
Ensure the user input matches one of the expected values:
Then your form handler:
This approach relies on PHP’s
in_arrayfunction for safety (and also exposes to the user your underlying column names, but given your application I doubt that’s a concern).Perform some input cleansing, such as:
Whilst we here quote the user input and replace any attempt by the user to escape from that quoting, this approach could be full of all sorts of flaws and vulnerabilities (in either PHP’s
mb_ereg_replacefunction or MySQL’s handling of specially crafted strings within a quoted identifier).It is far better if at all possible to use one of the above methods to avoid inserting user-defined strings into one’s SQL altogether.