繁体   English   中英

这个PHP代码安全吗?

[英]Is this PHP code secure?

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

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

  1. 即使使用非数字类型的参数,它们也会保护您
  2. 添加更多参数时,您不会冒失去输入卫生的风险
  3. 您的代码将更容易编写和阅读

这是一个例子(其中$dbPDO连接):

$stmt = $db->prepare('SELECT * FROM tbl_Persons WHERE Id = :id');
$stmt->execute(array(':id' => $_GET['post']));
$rows = $stmt->fetchAll();

有关PHP中参数化SQL语句的更多信息,请参阅:

这有点粗糙,但我没有立即看到任何会导致任何严重问题的事情。 您应该注意根据文档在is_numeric()接受十六进制表示法。 您可能想使用is_int()is_int()它。 为清楚起见,我建议使用参数化查询:

$sql = sprintf("SELECT col1 
                FROM tbl 
                WHERE col2 = '%s'", mysql_real_escape_string($post));

在这种情况下, $post将作为%s的值传入。

对于十六进制数字,例如'0xFF',is_numeric将返回true。

编辑:要解决这个问题,你可以这样做:

sprintf('%d', mysql_real_escape_string($post, $conn));
//If $post is not an int, it will become 0 by sprintf

请查看php.net上的代码片段获取更多信息。

你有正确的想法,但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注入的角度来看,你很好。

您没有将连接资源传递给mysql_real_escape_string()(但您似乎是使用mysql_select_db())。 连接资源和其他东西存储连接字符集设置,这可能会影响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

我觉得它看起来不错。

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM