-3

I always check/limit/cleanup the user variables I use in database queries

Like so:

$pageid = preg_replace('/[^a-z0-9_]+/i', '', $urlpagequery); // urlpagequery comes from a GET var

$sql = 'SELECT something FROM sometable WHERE pageid = "'.$pageid.'" LIMIT 1';

$stmt = $conn->query($sql);

if ($stmt && $stmt->num_rows > 0) {

    $row = $stmt->fetch_assoc();

    // do something with the database content

}

I don't see how using prepared statements or further escaping improves anything in that scenario? Injection seems impossible here, no?

I have tried messing with prepared statements.. and I kind of see the point, even though it takes much more time and thinking (sssiissisis etc.) to code even just half-simple queries.

But as I always cleanup the user input before DB interaction, it seems unnecessary

Can you enlighten me?

mowgli
  • 2,796
  • 3
  • 31
  • 68
  • 1
    It only takes more time and thinking when you're not used to it (as with most) – kero Jul 09 '14 at 19:26
  • 3
    Doesn't this seem like a lot more work than using prepared statements? – Daniel Lyons Jul 09 '14 at 19:28
  • Just putting preg_replace('/[^valid_chars_here]+/i', '', $var) before?.. No to me that's much easier and faster – mowgli Jul 09 '14 at 19:29
  • 1
    You can't always do a simple preg_replace(). Then you will use the appropriate escape function, i.e. if you've got to store last names like "O'Brien" or some such. It makes your code more complicated. So it's so much better to get into the habit of simply using prepared statements and don't have to care for every special case. – VMai Jul 09 '14 at 19:33
  • 1
    Preg_replace doesn't serve as an escape function. What's your protocol for when there is a " in place? Nested preg_replaces, to remove "bad" characters (which are those, by the way?) and one to escape quotes? Why are you reinventing the wheel? – Andrew Jul 09 '14 at 19:34
  • @Andrew It removes " too. I don't see when/why it should not? And I'm not using nested preg_replaces anywhere – mowgli Jul 09 '14 at 19:40
  • ignoring all of the sql injection stuff, prepared statements make sense if you need to run ONE query multiple times (e.g. bulk inserting data, where only the values change). preparing a statement saves you the sql parse/compile stage since it only has to be done once. If the query is going to be executed only once, then prepared statements are actually a waste of resources. But then, if you use them properly, you save a lot more time on not having to manually escape/prep things. – Marc B Jul 09 '14 at 19:41
  • Yes, in this one circumstance. But presumably you need to deal with more than simple numbers and letters in your database? Like, as VMai said, names like "O'Brien"? Or sentences, with punctuation? Or decimals? Or binary data, maybe? Or are you saying that this is the only query your system will ever need to handle? – Andrew Jul 09 '14 at 19:41
  • This exact scenario is just for pages on the site. Taking the url query, getting the page content from DB. And I always use letters/numbers/underscore only for pages name-id in db – mowgli Jul 09 '14 at 19:48

3 Answers3

4

The question would be how you defined "improve" in this context. In this situation I would say that it makes no difference to the functionality of the code.

So what is the difference to you? You say that this is easier and faster for you to write. That might be the case but is only a matter of training. Once you're used to prepared statements, you will write them just as fast.

The difference to other programmers? The moment you share this code, it will be difficult for the other person to fully understand as prepared statements are kind of standard (or in a perfect world would be). So by using something else it makes it in fact harder to understand for others.


Talking more about this little piece of code makes no sense, as in fact it doesn't matter, it's only one very simple statement. But imagine you write a larger script, which will be easier to read and modify in the future?

$id = //validate int
$name = //validate string
$sometext = //validate string with special rules

$sql = 'SELECT .. FROM foo WHERE foo.id = '.$id.' AND name="'.$name.'" AND sometext LIKE "%'.$sometext.'%"';

You will always need to ask yourself: Did I properly validate all the variables I am using? Did I make a mistake?

Whereas when you use code like this

$sql = $db->prepare('SELECT .. FROM foo WHERE foo.id = :id AND name=":name" AND sometext LIKE "%:sometext%"');
$sql->bind(array(
    ':id' => $id,
    ':name' => $name,
    ':sometext' => $sometext,
));

No need to worry if you done everything right because PHP will take care of this for you.

Of course this isn't a complex query as well, but having multiple variables should demonstrate my point.


So my final answer is: If you are the perfect programmer who never forgets or makes mistakes and work alone, do as you like. But if you're not, I would suggest using standards as they exist for a reason. It is not that you cannot properly validate all variables, but that you should not need to.

kero
  • 10,647
  • 5
  • 41
  • 51
  • I see your points. I have never seen name=":name" (array) in a query like that before.. Shouldn't I use bind_param('ssiiiss etc.')? – mowgli Jul 09 '14 at 19:43
  • The thing is also this.. people keep telling me: "why are you using prepared statement for this!? this is overkill!" and so it made me wonder.. and why use two different methods – mowgli Jul 09 '14 at 19:45
  • @mowgli This is no real working code, only demonstrating. But yes, you can do it via aliases like this or via `sssiss`, however you like. They're not wrong but not right either. As long as you don't really need to optimize perfomance, it doesn't matter for small queries like this – kero Jul 09 '14 at 19:47
  • @mowgli That's the PDO style with named parameters and the possibility to use arrays. It's a good thing. An to your next comment: No, it's not overkill. It's better ... – VMai Jul 09 '14 at 19:47
  • So, I should just use $sql->bind(array( ':id' => $id, ':name' => $name, ':sometext' => $sometext, )); ? It seems more logical and easy to understand with the eyes than 'ssiiisissss' where I have to count and measure every var (I fail at it constantly, and at every change I have to recount and remeasure) – mowgli Jul 09 '14 at 19:57
  • This really makes no difference, so use what you feel more comfortable with – kero Jul 09 '14 at 20:00
  • 1
    If I can just avoid that confusing ssiisi bind param, then prepared statements are not so bad ;) – mowgli Jul 09 '14 at 20:21
4

You will be better off using prepared statement consistently.

Regular expressions are only a partial solution, but not as convenient or as versatile. If your variables don't fit a pattern that can be filtered with a regular expression, then you can't use them.

All the "ssisiisisis" stuff is an artifact of Mysqli, which IMHO is needlessly confusing.

I use PDO instead:

$sql = 'SELECT something FROM sometable WHERE pageid = ? LIMIT 1';
$stmt = $conn->prepare($sql);
$stmt->execute(array($pageid));

See? No need for regexp filtering. No need for quoting or breaking up the string with . between the concatenated parts.

It's easy in PDO to pass an array of variables, then you don't have to do tedious variable-binding code.

PDO also supports named parameters, which can be handy if you have an associative array of values:

$params = array("pageid"=>123, "user"=>"Bill");
$sql = 'SELECT something FROM sometable WHERE pageid = :pageid AND user = :user LIMIT 1';
$stmt = $conn->prepare($sql);
$stmt->execute($params);

If you enable PDO exceptions, you don't need to test whether the query succeeds. You'll know if it fails because the exception is thrown (FWIW, you can enable exceptions in Mysqli too).

You don't need to test for num_rows(), just put the fetching in a while loop. If there are no rows to fetch, then the loop stops immediately. If there's just one row, then it loops one iteration.

while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {

    // do something with the database content

}

Prepared statements are easier and more flexible than filtering and string-concatenation, and in some cases they are faster than plain query() calls.

VMai
  • 10,156
  • 9
  • 25
  • 34
Bill Karwin
  • 538,548
  • 86
  • 673
  • 828
  • Thank you. Now I wonder if I should switch to PDO.. Just switched to mysqli – mowgli Jul 09 '14 at 20:16
  • Can you help me out here, very shortly? http://stackoverflow.com/questions/24670263/can-i-improve-my-pdo-method-just-started – mowgli Jul 10 '14 at 06:58
3

Prepared statements can sometimes be faster. But from the way you ask the question I would assume that you are in no need of them.

So how much extra performance can you get by using prepared statements ? Results can vary. In certain cases I’ve seen 5x+ performance improvements when really large amounts of data needed to be retrieved from localhost – data conversion can really take most of the time in this case. It could also reduce performance in certain cases because if you execute query only once extra round trip to the server will be required, or because query cache does not work.

Brought to you faster by http://www.mysqlperformanceblog.com/

I don't see how using prepared statements or further escaping improves anything in that scenario?

You're right it doesn't.

P.S. I down voted your question because there seems little research made before you asked.

Gumbo
  • 643,351
  • 109
  • 780
  • 844
beingalex
  • 2,416
  • 4
  • 32
  • 71
  • Usually my sites/pages/databases/queries are very small and simple. But occasionally I need to collect 1000 rows.. perhaps THEN I should use prepared statements, no? – mowgli Jul 09 '14 at 19:36
  • No. :) That kind of size would not warrant a prepared statement if it's a query from one table of about 1000 rows. Are you having speed issues? – beingalex Jul 09 '14 at 19:38
  • I don't think so no. Seems pretty quick – mowgli Jul 09 '14 at 19:44
  • Then you don't need to worry about writing a prepared statement. :) Go forth. – beingalex Jul 09 '14 at 19:44
  • But you know, I want to do what's best practice, also regarding future projects in different scales. Want to use a good habit from start – mowgli Jul 09 '14 at 19:51