一个简单的问题:以下PHP代码是否安全?还有你认为我可以或应该添加的东西吗?
$post = $_GET['post'];
if(is_numeric($post))
{
$post = mysql_real_escape_string($post);
}
else
{
die("NAUGHTY NAUGHTY");
}
mysql_select_db("****", $*****);
$content = mysql_query("SELECT * FROM tbl_***** WHERE Id='" . $post . "'");
答案 0 :(得分:9)
在这种特殊情况下,我猜is_numeric
可以使您免于SQL注入(尽管您仍然可以打破SQL语句,参见Alex的回答)。但是,我真的认为你应该考虑使用参数化查询(也就是预备语句),因为:
以下是一个示例(其中$db
是PDO连接):
$stmt = $db->prepare('SELECT * FROM tbl_Persons WHERE Id = :id');
$stmt->execute(array(':id' => $_GET['post']));
$rows = $stmt->fetchAll();
有关PHP中参数化SQL语句的更多信息,请参阅:
答案 1 :(得分:2)
这有点粗糙,但我没有立即看到任何会导致任何严重问题的事情。您应该注意根据文档在is_numeric()
内接受十六进制表示法。您可能想要使用is_int()
或投射它。为清楚起见,我建议使用参数化查询:
$sql = sprintf("SELECT col1
FROM tbl
WHERE col2 = '%s'", mysql_real_escape_string($post));
在这种情况下,$post
将作为%s
的值传入。
答案 2 :(得分:2)
is_numeric将返回true,例如'0xFF'。
编辑:要解决此问题,您可以执行以下操作:
sprintf('%d', mysql_real_escape_string($post, $conn));
//If $post is not an int, it will become 0 by sprintf
请查看代码段here on php.net以获取更多信息。
答案 3 :(得分:2)
您有正确的想法,但is_numeric()可能无法按预期运行。
试试这个测试:
<?php
$tests = Array(
"42",
1337,
"1e4",
"not numeric",
Array(),
9.1
);
foreach($tests as $element)
{
if(is_numeric($element))
{
echo "'{$element}' is numeric", PHP_EOL;
}
else
{
echo "'{$element}' is NOT numeric", PHP_EOL;
}
}
?>
结果是:
'42' is numeric
'1337' is numeric
'1e4' is numeric
'not numeric' is NOT numeric
'Array' is NOT numeric
'9.1' is numeric
如果你正在寻找通常被称为数值的东西,那么1e4可能不是你的sql server理解的东西。从SQL注入的角度来看,你很好。
答案 4 :(得分:2)
您没有将连接资源传递给mysql_real_escape_string()(但您似乎是使用mysql_select_db())。连接资源以及其他内容存储连接字符集设置might affect the behavior of real_escape_string() 要么不将资源传递到任何地方,要么(最好)总是传递它,但是不要让它比通过混合传递资源更糟糕。
我的书中的“安全性”还包括代码是否可读,“易于理解”和“直截了当”。在示例中,当您将id视为SELECT查询中的字符串时,您至少必须向我解释为什么您具有!numeric -> die
分支。我的反驳论据(如示例所示;在你的上下文中可能是错误的)将是“为什么要打扰?SELECT只是不会返回任何非数字id的记录”,这会将代码减少到
if ( isset($_GET['post']) ) {
$query = sprintf(
"SELECT x,y,z FROM foo WHERE id='%s'",
mysql_real_escape_string($_GET['post'], $mysqlconn)
);
...
}
自动消除了您可能遇到的麻烦,因为is_numeric()的行为与您预期的不同(如其他答案中所述)。
编辑:关于在生产代码中经常/早期使用die()
,有一些话要说。它适用于测试/示例代码,但在实时系统中,您几乎总是希望将控制权交还给周围的代码,而不是仅仅退出(因此您的应用程序可以优雅地处理错误)。在开发阶段,您可能希望提前纾困或进行更多测试。在这种情况下,请查看http://docs.php.net/assert。
您的示例可能有资格获得断言。如果断言被停用,它不会中断,但是当传递非数字参数时,它可能会给开发人员提供更多信息,说明为什么它不能按预期(由其他开发人员)工作。但是你必须小心将必要的测试与断言分开;它们不是银子弹。
如果你觉得is_numeric()是一个必不可少的测试,你的函数(?)可能会返回false,抛出一个异常或其他东西来表示这个条件。但对我来说,早期的死()是一个简单的出路,有点像一个无能的负鼠,“我不知道现在该做什么。如果我玩死了也许没有人会注意到”; - )
强制性暗示准备好的陈述:http://docs.php.net/pdo.prepared-statements
答案 5 :(得分:1)
我觉得它看起来不错。
访问数据库时,我总是使用查询绑定,这可以避免出现问题,如果我忘记了我的字符串。