4

Is it safe to only use intval to sanitize the user input for a database select, as in the following example?

$id = intval($_GET['id']);

$query = "SELECT * FROM table WHERE id='$id'";
3
  • 2
    Possibly. Why do it though when you can do it the right way and not accidently have code later that needs to continue to do it the wrong way? "Ok, so I need to add an and statement with a parameter... $type = $_GET['somestring']; ... I'll just continue the style used here. $query = "select * from table where id='$id' and str='$type'; ... DOH!"). Do it the right way from the start.
    – user40980
    Jul 1 '15 at 18:09
  • I get your point @MichaelT but it doesn't really apply in this case (promise!). I thought of it as pretty neat if it is actually as safe as I think it is. But is it?
    – Dahoon
    Jul 1 '15 at 18:21
  • 1
    In certain cases, you can only use a non-negative integer (limits) - in that case, intval will still allow illegal input through.
    – Sam Dufel
    Jul 8 '15 at 15:30
8

intval is guaranteed to produce an integer (it returns 0 if it "fails"), and an integer is a safe thing to use in a MySQL query. This answer https://stackoverflow.com/a/3208089/458614 on SO agrees:

Yes, intval() is safe. There is absolutely no way to perform an SQL injection when the parameter is converted to integer, because (obviously) the format of an integer does not allow putting SQL keywords (or quotes, or whatever) in it.

I suspect the same goes for uses other than SQL.

1
  • 1
    intval() will also return a 1 if someone managed to pass an object value into it. So if 0 or 1 a valid inputs you cannot rely on it. Aug 14 '16 at 8:54
3

Is it safe to only use intval to sanitize the user input for a database select, as in the following example?

Short answer: No.

OK, intval() will always deliver you an integer value which will not break your SQL.

The problem is that, whilst the value returned is an integer, is it a valid one? Can you guarantee that the value returned by intval (e.g. 0) will never correspond to a real data record? With code that does no further checking, you could inadvertently create a record with a key of 0 and then have many users access the same record, all because they're [all] entering invalid numeric values. Not good.

All for the want of a polite error message like "ID must be numeric".

1

Rule of thumb is: If the input comes from anywhere else than from the code itself, the data provided is to be considered 'tainted'. You will have to make sure the value is what you expect and would not mess up your SQL statement (injection).

Though I question why you're not using/utilizing PDO and use markers in your statement so these things are handled automatically.

But I would choose to do something like this (using a ternary operator):

$id = (is_numeric($id) && $id > 0) ? $id : false;

if ($id !== false) {
   ... code goes here ...
}

It doubles the possibility of $id being a realistic integer.

3
  • What could $id be in this case such that casting it with intval and then using it in your query could be harmful?
    – tobek
    Dec 17 '15 at 10:09
  • @tobek: Now? Nothing. PHP's intval is guaranteed to return an integer. But your code might change in the future (e.g. if the id becomes an alphanumerical string instead) and you remove the call to intval, or if you redefined intval. There's no reason not to use a prepared statement when dealing with user input. Dec 17 '15 at 20:51
  • @tobek There is no harm IF (any any case) the ID would then be casted into the integer value 0 and you know ID 0 does not exist. I just like to be sure and would approach the 'challenge' this way. Yes, some extra lines of code, but better safe than sorry ;) Dec 21 '15 at 12:47
0

First, check if $id is a valid numeric value ( using is_numeric ) then finally cast the $id to type ( int ).

0

Ints are by definition safe for any situation I can think of, though for use as a param to a CLI command, ensure they aren't negative as the leading hyphen may mean they become taken as flags instead of params; and for cases where there's a storage size limits, beware of bounds as overflows may be unfortunate.

However, I'd be inclined to create a bunch of user-input validation methods, if you are doing this often. Something like:

public function getValidInt(
    string $name,
    int $minValue=PHP_INT_MIN,
    int $maxValue=PHP_INT_MAX,
    int $defaultValue=0
) {
    assert($minValue < $maxValue, 'Min bound must be less than max.');
    if (!array_key_exists($name, $_GET)) {
        return $defaultValue;
    }
    $userValue = (int)$_GET[$name];
    // Could instead return $defaultValue if it's out of bounds.
    return min($maxValue, max($minValue, $userValue));
}

This makes it super-easy to get yourself untainted, bounds-checked values with a valid default value if they are not specified.

For strings, this might look like:

public function getValidString(
    string $name,
    string $validationRegex='.*',
    int $minLength=0,
    int $maxLength=PHP_INT_MAX,
    int $defaultValue=''
)

...and so forth.

Adding such things early on in your project means you'll be thinking about every user input, and will not amass a huge tech debt task of "validate all user input" as you near the end of the project. Everything will already be correct; strings will be whitelisted, ints will be bounds-checked, and so forth.

Escaping and detainting on input should ideally be treated as a separate concern from escaping for use in a database, display as HTML, etc. Removing malicious HTML tags from strings is something you should be doing on input (there should be nobody called <span onclick="alert('whee')">Fred</span> stored in your database, even if you have not yet displayed their name to the screen. Escaping before you throw a variable to the HTML or MySQL or commandline should be your final last-chance confirmation that you've ensured save variables, not be your only check.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.