posted to this is not a weblog
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

if you want to be able to mark your comment as authentic or delete it later, include your e-mail address (it won't be shown on the site). you'll be e-mailed instructions with a copy of your comment.

basic html allowed; be nice
required


optional, gravatar-ized


optional