Talking about the dynamic column and not the dynamic value for a moment...
Schema object identifiers (columns, tables, etc.) can't be parameters. So sometimes in cases like this you might have to step outside of the "always use parameters" mantra and dynamically build your query from variables. For example:
$sql = "SELECT * FROM songs WHERE $columnName LIKE '%SEARCH%'";
Then the million dollar question to assuage our fears of SQL injection is:
Where does $columnName
come from?
It should never come from user input. Fortunately, column names are finite and known ahead of time. So you can give the user the ability to specify one, but you have data you can validate that input against. Consider a whitelist approach such as:
- User specifies a column name.
- You compare against a known list of columns (either hard-coded or pulled form the database schema).
- If there is no match, return an error.
- If there is a match, use the matched element from the known list.
This allows you to dynamically build your SQL, but not by using anything which was ever user-editable. Santizing user input is one thing, but not having to rely on it in the first place and always using known good values is something else entirely. After all, someone may balk at this:
$sql = "SELECT * FROM songs WHERE $columnName LIKE '%SEARCH%'";
But it's hardly SQL-injectable in this context:
$columnName = "title";
$sql = "SELECT * FROM songs WHERE $columnName LIKE '%SEARCH%'";
Which is no different from:
$sql = "SELECT * FROM songs WHERE title LIKE '%SEARCH%'";
The value being searched ('%SEARCH%'
in this case, which would change to use whatever your database API uses for adding parameters to a query) should certainly be a parameter and not concatenated directly into the query. But schema object identifiers are a different story.