10

I have a page (index.php) that takes a GET variable from the URL and checks it for security purposes. This GET variable should only be an integer. I am using the following code to check this, but in all instances, integer or not, I get the index.php page. The header never appears. After this code, the rest of the page appears starting with the html tag.

PHP:

<?php ob_start(); session_start();
$q=trim($_GET['q']);
if (!is_numeric($q)){
header("HTTP/1.0 404 Not Found");
}
?>
kirby
  • 3,981
  • 14
  • 41
  • 54
  • 2
    I don't know if you're aware already, but you should add an `exit();` after the `header(..)`. Otherwise the script will continue to print out the page contents. – laher Jan 22 '12 at 19:31
  • Reading [the documentation](http://php.net/manual/en/function.is-int.php) would show you, that `is_int()` checks the type of the variable, so string with "`123`" into `is_int()` will not return `true`, as this is string. Also why did you tag your post with `http-status-code-404`? – Tadeck Jan 22 '12 at 19:32
  • @Tadeck tag removed. thank you for that documentation. im now using is_numeric and it still isnt working – kirby Jan 22 '12 at 19:34
  • @amir75 you are right. it worked! is it ok if i use `die();` instead? – kirby Jan 22 '12 at 19:45

3 Answers3

31

It won't be an integer if it's passed in a query string.

Try is_numeric()

AlienWebguy
  • 76,997
  • 17
  • 122
  • 145
5

There is a better way to do this, which is casting to int:

$q = (int) $_GET['q'];

The is_int is behaving as expected. Because GET arguments are always strings. Try var_dumping them.

Joep
  • 651
  • 3
  • 10
  • 2
    Come to think of it, AlienWebguy's answer is probably better as you'll be able to filter out any 'weird' values... – Joep Jan 22 '12 at 19:29
1

Sometimes you want to validate input which should be number but in $_GET or $_POST you will get it as string. is_numeric() may be problematic, because it allows hex, binary and octal format (from manual):

Thus +0123.45e6 is a valid numeric value. Hexadecimal (e.g. 0xf4c3b00c), Binary (e.g. 0b10100111001), Octal (e.g. 0777) notation is allowed too but only without sign, decimal and exponential part.

You can't use is_int() as it's only for integer values (not string!) so... You can validate numbers which are string AND integer that way:

/**
 * Validate integer.
 */
class IntegerValidator
{
    /**
     * Run validation.
     * @param string $value
     * @return bool
     */
    public static function isIntVal(string $value): bool
    {
        if (!self::hasValidIntegerFormat($value)) {
            return false;
        }

        return !self::hasLeadingZero($value);
    }

    /**
     * Check if given string looks like valid integer. Negative numbers allowed.
     * @param string $value
     * @return bool
     */
    private static function hasValidIntegerFormat(string $value): bool
    {
        return (bool) preg_match('/^-?[0-9]+$/', $value);
    }

    /**
     * Check if given number has leading 0. Thus it's invalid integer.
     * @param string $number
     * @return bool
     */
    private static function hasLeadingZero(string $number): bool
    {
        return self::extractFirstDigit($number) === 0;
    }

    /**
     * Extract first digit from given number.
     * @param string $number
     * @return int
     */
    private static function extractFirstDigit(string $number): int
    {
        return self::isNegativeInteger($number)
            ? (int) $number[1]
            : (int) $number[0];
    }

    /**
     * Check if number is negative integer. ie. starts with minus sign on the beginning.
     * @param string $number
     * @return bool
     */
    private static function isNegativeInteger(string $number): bool
    {
        return $number[0] === '-';
    }
}

var_dump(IntegerValidator::isIntVal('123'));   // true
var_dump(IntegerValidator::isIntVal('0123'));  // false
var_dump(IntegerValidator::isIntVal('-0123')); // false
var_dump(IntegerValidator::isIntVal('-123'));  // true

It is also possible to override is_int() function, using override_function() but it still may be useful in original version.

instead
  • 3,101
  • 2
  • 26
  • 35