on jul 18th, 2005
tagged nerd, php, sql
and
never commented on
share this page
i ran into this little annoyance today in php that kept me scratching my head for a while.
i narrowed it down to a small test case which the php security@ contact says is not a bug, but is by design. seems like quite a bad design:
jcs@hypotenuse:~> cat test.php <?php $var = 0; print ($var == "blah" ? "yes" : "no") . "\n"; ?> jcs@hypotenuse:~> php test.php yes
the reason being, php attempts to convert "blah" into an integer, fails, gives it 0, and then compares 0 to 0, returning true.
it seems to me it would make more sense in an integer-to-string comparison to convert the integer to a string and compare. perl seems to do it that way:
jcs@hypotenuse:~> cat test.pl #!/usr/bin/perl $var = 0; print "" . ($var eq "blah" ? "yes" : "no") . "\n"; jcs@hypotenuse:~> perl test.pl no
i ran into the issue in php while comparing data pulled from sql to a string. so even if you aren't comparing to a variable that was explicitly assigned an integer ($var = 5;) you have to make sure the data from sql didn't come from an INT column.
jcs@hypotenuse:~> cat test.php
<?php
require_once("global.php");
db_connect("kiev");
mssql_query("CREATE TABLE #test (u INT)");
mssql_query("INSERT INTO #test VALUES (5)");
mssql_query("INSERT INTO #test VALUES (0)");
$result = mssql_query("SELECT u FROM #test");
while ($row = mssql_fetch_row($result))
if ($row[0] == "blah")
$row[0] . " == blah\n";
else
print $row[0] . " did not match\n";
?>
jcs@hypotenuse:~/dls/www> php test.php
5 did not match
0 == blah
yes, that code would not make much sense since you know you're pulling from an INT column and then comparing to a string.
but that same code with 'u' as a VARCHAR field yields different results.
now consider pulling from a VARCHAR column, then pushing data to an array and reading it later. if one of those values looks like a number, the array key automagically becomes an integer, even though the array is an associative one.
jcs@hypotenuse:~/dls/www> cat test.php
<?php
require_once("global.php");
db_connect("kiev");
$vars = array();
$result = mssql_query("CREATE TABLE #test (a VARCHAR(5))");
$result = mssql_query("INSERT INTO #test VALUES ('abc')");
$result = mssql_query("INSERT INTO #test VALUES ('0')");
$result = mssql_query("INSERT INTO #test VALUES ('hello')");
$result = mssql_query("SELECT a FROM #test");
while ($row = mssql_fetch_row($result)) {
if ($row[0] == "blah")
print $row[0] . " == blah\n";
else
print $row[0] . " did not match\n";
$vars[$row[0]] = 1;
}
print "\n";
foreach (array_keys($vars) as $var)
if ($var == "blah")
print $var . " == blah\n";
else
print $var . " did not match\n";
?>
jcs@hypotenuse:~/dls/www> php test.php
abc did not match
0 did not match
hello did not match
abc did not match
0 == blah
hello did not match
so if you're matching any tainted data against a string, better make sure it didn't magically get converted to an integer behind the scenes. if it did, or even may have, you have to either explicitly convert it to a string before comparing it against another string:
$var = sprintf("%s", $var);
or use strcmp(), or use php's === operator which doesn't compare different types. the original code, using ===:
jcs@hypotenuse:~> cat test.php <?php $var = 0; print ($var == "blah" ? "yes" : "no") . "\n"; print ($var === "blah" ? "yes" : "no") . "\n"; ?> jcs@hypotenuse:~> php test.php yes no
fun, isn't it.
leave the first comment or contact me