MySQL input/output validation

While talking about SQL injection one may ask how to secure their webapp. Here’s a function I currently use, adapted from php.net. DO NOT trust my lousy functions ;-) . Just because I believe it’s secure doesn’t mean it is ;-) . It bases on mysql_real_escape_string() so make sure you run PHP >= 4.3.0.


function check_gpc_input($value)
{
if(!empty($value))
{
// truncation (see comments)
$value = substr($value,0,20);
}
// Stripslashes if magic quotes enabled
if (get_magic_quotes_gpc())
{
$value = stripslashes($value);
}
// Quote if not a number
if (!ctype_digit($value))
{
$value = "'" . mysql_real_escape_string($value) . "'";
}
else
{
$value = intval($value);
}
return $value;
}

usage:
$cat = check_gpc_input($_GET["id"]);
$query = "SELECT id,name FROM users WHERE id = ".$cat."";

Note that this function is only for GPC (GET/POST/COOKIE) input. Do not use it to validate output from your database or other input. Instead make sure you use htmlentities() if you output data to prevent XSS.

Also consider that your validation depends on your webapp. I would advice to take those examples only as idea how your functions could look like.

References: sla.ckers, Ronalds secure programming flowchart, 0×000000 thread

8 Responses to “MySQL input/output validation”

  1. .ﻩﻨﺮﻪﺴ Says:

    Hmmm – why the truncation? Also it should be pointed out that mysql_real_escape_string can’t be used in any case.

    I winder how MySQL reacts on quotes encoded ans hex entities – do you have any info on that?

    Greetings,
    .ﻩﻨﺮﻪﺴ

  2. Reiners Says:

    I just added the truncation to make clear that you should truncate at first before you do other sanitizing. As seen in the added sla.ckers link, one may add the truncation at the wrong place. But you are right, it’s not neccessary. However it can protect the used php functions (we have to trust) against BoF or alike, if you know your input is going to be not longer than a fixed value anyway.
    As far as I’ve tested, mysql query fails with entities in the mysql syntax. There is no decoding inside the query or a quoted value.

    Thanks for your input, really appreciated. I thought my blog is quite unknown yet, though I didnt raise any attention because theres not much to read about yet and even the published articles need a finish ;) .

  3. .ﻩﻨﺮﻪﺴ Says:

    I’d rather suggest to chose validation over truncation. If you deal with IDs you most times get integers (coming in as string when user submitted) – so you can chose is_numeric() or ctype_digit().

  4. Reiners Says:

    thanks again for your input .mario! I changed is_int() to ctype_digit() because int’s come in as string, you’re right.
    But I still think that if you decide to truncate data (which isn’t neccessary) it should be the first thing you do.
    Additionally I replaced intval() with a type cast (int) because I couldn’t find out any difference, but its one function less we have to trust.
    What do you think?

  5. .ﻩﻨﺮﻪﺴ Says:

    I don’t really agree with the typecast. You normally cast when you want to make sure that something that’s supposed to be of a certain type will really be of that certain type (like you need 1 – even if it’s coming in as “1″).

    When dealing with user generated content you might expect anything – so a typecast is pretty useless. I think especially when facing potential SQLI there’s nothing else that validation. If that fails the probability is 90+% sure that something wnet wrong – so why casting in those cases? :)

    Ah – and never forget – I stepped into this trap myself some times – ctype_digit returns the incoming string – not true. Might produce evil surprises especially when dealing with unit tests :)

    Greetings,
    .mario

  6. Reiners Says:

    Ok I see your point with the type cast and changed it back to the original intval() ;) . But actually we could take it out completly if we trust ctype_digit() I think.
    As far as I’ve tested ctype_digit – it does return true/false, just like the php manual says so I see no problem there, or am I missing something?

  7. raz0rname Says:

    Nice function, but why don’t you use is_numeric() instead of ctype_digit()? is_numeric() is commonly used in such cases. I have written a function that checks user input, have a look if you are interested (the post is in Russian, but i guess we both speak PHP :)
    http://raz0r.name/releases/funkciya-dlya-obrabotki-vxodyashhix-dannyx/

  8. Reiners Says:

    hey raz0r,
    there has been some discussion on is_numeric() on sla.ckers here. although we found no example how to exploit it, I agreed with Mordred that hex numbers should not be accepted if you expect integers only (which is a common case for SQL input).

    greetings,
    Reiners

Leave a Reply