user1596165 user1596165 - 3 months ago 14
MySQL Question

Security for prepared SQL statement with REGEX in query

I'm trying to use a prepared PDO statement for use in a database query that looks like the following, where the placeholders variables are represented by the numbers in quotes, i.e., "123" and "456":

SELECT `user_ID` as `ID`
FROM `usermeta`
WHERE (`meta_key` = 'custom_fields')
AND (`meta_value` REGEXP '.*"ABC";.*s:[0-9]+:"123".*')
AND (`meta_value` REGEXP '.*"DEF";.*s:[0-9]+:"456".*')


My question is, would the best practice be to bind to the whole REGEX expression, or just the "123" and "456" variables (is that even possible in a REGEX expression), or something wholly different?

In other words, which is preferred, this:

SELECT `user_ID` as `ID`
FROM `usermeta`
WHERE (`meta_key` = 'custom_fields')
AND (`meta_value` :REGEXP1)
AND (`meta_value` :REGEXP2)

$stmt->bindParam(':REGEXP1', "REGEXP '.*"ABC";.*s:[0-9]+:"123".*'");
$stmt->bindParam(':REGEXP2', "REGEXP '.*"DEF";.*s:[0-9]+:"456".*'");


Or this? (I know there would be some issues with double quotes surrounding the placeholder.)

SELECT `user_ID` as `ID`
FROM `usermeta`
WHERE (`meta_key` = 'custom_fields')
AND (`meta_value` REGEXP '.*"ABC";.*s:[0-9]+:":value1".*')
AND (`meta_value` REGEXP '.*"DEF";.*s:[0-9]+:":value2".*')

$stmt->bindParam(':value1', '123');
$stmt->bindParam(':value2', '456');


Thank you.

Answer

Placeholders can't be quoted. Simple as that:

SELECT ... WHERE foo = ?
SELECT ... WHERE foo = '?'

The first one is a placeholder, and works as expected. The other one is testing for equality against the character "question mark". It's not a placeholder anymore.

And then there's the problem of ? being a regex metacharacter as well. If placeholders COULD be quoted, then given

SELECT ... WHERE foo REGEXP '^.?' 

would that ? be a query placeholder, or is it the regex "zero-or-one" range operator?

If you want to use placeholders in regexes, you have to "build" the regex pattern

SELECT ... WHERE foo REGEXP concat('^.', ?)

Exactly the same way as you would have to build a LIKE pattern:

SELECT ... WHERE foo LIKE '%?%' // wrong
SELECT ... WHERE foo LIKE concat('%', ?, '%') // right
Comments