这个PHP代码安全吗?

时间:2010-02-18 14:32:10

标签: php

一个简单的问题:以下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 . "'");

6 个答案:

答案 0 :(得分:9)

在这种特殊情况下,我猜is_numeric可以使您免于SQL注入(尽管您仍然可以打破SQL语句,参见Alex的回答)。但是,我真的认为你应该考虑使用参数化查询(也就是预备语句),因为:

  1. 即使使用非数字类型的参数
  2. ,它们也会保护您
  3. 在添加更多参数时,您不会冒失去输入卫生的风险
  4. 您的代码将更容易编写和阅读
  5. 以下是一个示例(其中$dbPDO连接):

    $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)

我觉得它看起来不错。

访问数据库时,我总是使用查询绑定,这可以避免出现问题,如果我忘记了我的字符串。