7

For a search feature I wrote a MySQL query to be executed by a PHP script. I'm not doing a fulltext search. Instead, I'm doing a search using the following method:

... WHERE field LIKE '%etc%' AND field REGEXP '[[:<:]]etc[[:>:]]'

Now, my idea is to prepare these dynamic values in PHP, like:

$word = '2*3%5_1^0'; // just an example

$wordLike = strtr($word,array('\\'=>'\\\\','%'=>'\\%','_'=>'\\_'));
// instead of my old solution:
// $wordLike = preg_replace('~([%_])~', '\\\\$1', $word);
$wordLike = $db_con->escape('%' . $wordLike . '%');

$spaces = '[[:blank:]]|[[:punct:]]|[[:space:]]';
// I'm not sure about the difference between blank & space, though

$wordRX = preg_quote($word);
$wordRX = $db_con->escape('(^|'.$spaces.')'.$wordRX.'($|'.$spaces.')');
// instead of my old solution:
// $wordRX = $db_con->escape('[[:<:]]' . $wordRX . '[[:>:]]');

and then use these values like in…

... WHERE field LIKE '$wordLike' AND field REGEXP '$wordRX'

which, with this example input, results in

...
WHERE field LIKE '%2*3\\%5\\_1^0%' AND
field REGEXP '[[:<:]]2\\*3%5_1\\^0[[:>:]]`

A couple of notes…

  • In my actual code I'm making it handle multiple words, this is just the pseudo code.
  • The method I'm using to search the word(s) -with LIKE & REGEXP together- was the fastest one among the approaches I tried.
  • I know I should use PDO instead, please don't input anything about that unless it's relevant to my issue

Q1: Is this the right way to go?
Q2: Is this secure enough against SQL injections?


Some additional info

About MySQL REGEXP

Following characters are escaped by preg_quote()

. \ + * ? [ ^ ] $ ( ) { } = ! < > | : -

Following is the list of [occasionally] special characters in REGEXP

. \ + * ? [ ^ ] $ ( ) { } | -

There are also additional constructs in REGEXP but they're all surrounded by single/double brackets, and because I know all the brackets will be escaped by preg_quote() I feel like I shouldn't be concerned about them.

About MySQL LIKE

The only 2 special characters in LIKE are

_ %

So escaping them seems enough a workaround.
Please correct me if I'm missing anything.

Anas
  • 5,622
  • 5
  • 39
  • 71
inhan
  • 7,394
  • 2
  • 24
  • 35
  • 1
    `preg_quote` is for PHP's `preg_*` regex functions, so I doubt it works for MySQL regex function because that is a different regex language/dialect. Also take care that the like escape you do is broken and can be easily fooled. – hakre Aug 16 '12 at 19:45
  • 2
    Why do you run a LIKE and REGEXP query? – hakre Aug 16 '12 at 19:49
  • @hakra **preg_quote()** does its job, actually. But I don't get what you mean by fooling the `LIKE` escape. Can you clarify that, please? – inhan Aug 16 '12 at 21:13
  • Imagine an input like `'\\%'` for example. So it can be fooled. I don't know if a good answer to that problem exists on the site already. You'll get so far a +1 from me for your question because it's written well and you take this seriously. I will try to find some useful related questions, probably there is more information to grasp. – hakre Aug 16 '12 at 21:26
  • @hakra Thanks for the +1 :) and good point on `'\\%'`. I'm working on it. – inhan Aug 16 '12 at 22:35
  • @hakra I think I should better stick with **strtr()** (http://php.net/manual/en/function.strtr.php). I edited my code. – inhan Aug 16 '12 at 23:11
  • 1
    I think we should create a canonical question on how to escape/encode for Mysql LIKE and Mysql REGEXP out of this and as it does not exist so far (at least how far I know) might be worth a bounty. – hakre Aug 17 '12 at 08:45

3 Answers3

2

Appart from what you mention mysql_real_escape_string() function should do fine for sanitization against SQL injection.

You just have to properly escape whatever user input using the appropiate escaping function(s), if you picture it as chained processing blocks processing this user input you will know in which order (from last to first) and what to escape/unescape and when, and you should be okay as far as securing a clean input goes (validation is a different issue).

And, as you already seem to know, quote() on PDO or Mysqli prepare() are a better approach.

NotGaeL
  • 8,344
  • 5
  • 40
  • 70
1

try this use mysql_real_escape_string()

$word = '2*3%5_1^0';

$query = 'SELECT * FROM TABLE_NAME WHERE field REGEXP "(.*)[[:<:]]'.mysql_real_escape_string($word).'[[:>:]](.*)" ';
Chintan
  • 1,204
  • 1
  • 8
  • 22
1
function clean($str) {
    $str = @trim($str);
    if(get_magic_quotes_gpc()) {
        $str = stripslashes($str);
    }
    return mysql_real_escape_string($str);
}

then:

$word = clean($_POST['whatever post']);

then trim word and your good to go. what this php function does is take all literals and turns them into strings so no one can lets say delete your db etc

Rostyslav Dzinko
  • 39,424
  • 5
  • 49
  • 62
Dnaso
  • 1,335
  • 4
  • 22
  • 48
  • I'm using the `LIKE` function, in which underscore and percentage signs have special meanings. The `REGEXP`, on the other hand is another issue. So this question is not about escaping **any data** but thanks for your input :) – inhan Aug 20 '12 at 23:18