【问题标题】:Zend Database Adapter - Uncaught Exception - Stack Trace Reveals Username and PasswordZend 数据库适配器 - 未捕获的异常 - 堆栈跟踪显示用户名和密码
【发布时间】:2012-03-05 22:36:32
【问题描述】:

我正在使用Zend_Db_Adapter,特别是Zend_Db_Adapter_Pdo_Abstract。我想这个问题也会扩展到其他适配器。当PDOException 被抛出时,它是“未被捕获”的,并且在许多情况下,堆栈跟踪会显示用户名和密码。

我已验证以下 PDO 异常都在堆栈跟踪中显示凭据:

  • SQLSTATE[HY000] [2005] 未知的 MySQL 服务器主机 ...snip...
  • SQLSTATE[HY000] [2013] 在“读取授权数据包”时失去与 MySQL 服务器的连接
  • SQLSTATE[08004] [1040] 连接过多
  • SQLSTATE[28000] [1045] 用户 ...snip... 的访问被拒绝(使用密码:YES)

当出现错误时,我的生产站点不显示堆栈跟踪,我仍然希望在我的开发环境中查看这些错误的堆栈跟踪,我只是不希望用户名和密码以明文形式显示。

【问题讨论】:

  • 我有两个赞成票和两个反对票。我希望它需要发表评论并投反对票。我真的很想知道为什么它被否决了。
  • 不确定...downvotes 不是来自我。也许对您要查找的内容更加清晰和简洁?真的没有看到值得一票否决的东西
  • 您应该使用 try/catch 来抑制此错误,但即便如此,我仍然相信任何错误消息都不应该公开显示密码。这个问题应该提交给 PDO 团队,请在这里投票:bugs.php.net/bug.php?id=62184

标签: php mysql zend-framework pdo


【解决方案1】:

我会通过不解决它来解决这个问题......让我解释一下:

目前无法从未捕获的异常中禁用堆栈跟踪。 PHP 不允许你这样做。

所以,与其尝试禁用它,我不会让异常未被捕获...我会安装一个exception handler,然后它会记录回溯信息。我不会在屏幕上显示它。我不会检查它在哪个环境中。我不会检查请求信息。我只是将它记录到一个文件中,然后显示一个通用的 500 服务器错误页面。

现在,在您的处理程序中,您可以选择性地显示调用信息,因此您可以选择是否记录参数信息:

set_exception_handler(function($exception) {
    $log = array(
        'message' => $exception->getMessage(),
        'trace' => array(),
    );
    foreach ($exception->getTrace() as $item) {
        $trace = isset($item['class']) ? $item['class'] . $item['type'] : '';
        $trace .= $item['function'] . '()';
        $log['trace'][] = $trace;
    }
    save_to_log($log);
});

但我将未捕获的异常视为您的应用程序中存在错误的标志。你应该找到它们并修复它们。如果你得到的足够多以至于你担心在页面中显示参数,那么你真的需要首先解决存在未捕获异常的事实......

编辑以下是所发生情况的演示:

class Foo {
    public function doSomething($user, $password) {
        throw new Exception('Something Went Wrong!');
    }
}

$f = new Foo();

$f->doSomething('user', 'passw');

on CodePad 结果:

<br />
<b>Fatal error</b>:  Uncaught exception 'Exception' with message 'Something Went Wrong!' in /code/MxH9Ls:4
Stack trace:
#0 /code/MxH9Ls(10): Foo-&gt;doSomething('user', 'passw')
#1 {main}
  thrown in <b>/code/MxH9Ls</b> on line <b>4</b><br />

但是,使用异常处理程序(修改为打印而不是日志):

set_exception_handler(function($exception) {
    $log = array(
        'message' => $exception->getMessage(),
        'trace' => array(),
    );
    foreach ($exception->getTrace() as $item) {
        $trace = isset($item['class']) ? $item['class'] . $item['type'] : '';
        $trace .= $item['function'] . '()';
        $log['trace'][] = $trace;
    }
    echo $log['message'] . "\n";
    foreach ($log['trace'] as $trace) {
        echo " - $trace\n";
    }
});

class Foo {
    public function doSomething($user, $password) {
        throw new Exception('Something Went Wrong!');
    }
}

$f = new Foo();

$f->doSomething('user', 'passw');

On CodePad 产生:

Something Went Wrong!
 - Foo->doSomething()

【讨论】:

  • 我将尝试您的解决方案,但可能需要几天时间才能完成。谢谢!
  • 更多背景信息:前三个是我的托管服务出现数据库问题时发生的错误。它们很少发生,但即使有一次也会导致数据库身份验证凭据被泄露。
  • @Sonny:仍然,您应该捕获这些并相应地处理(显示 500 错误页面,从缓存中读取等)...
  • 我同意。我将使用您的答案作为完成我需要的指南。谢谢!
【解决方案2】:

您可以尝试两种方法,第一种可能是最好的解决方案是捕获异常并将它们传递给自定义异常类。该类还将采用适配器的实例,如下所示:

未经测试的示例

class myPDOException extends Exception
{
     public function __construct($message = '', $code = 0, Exception $previous = null, Zend_Db_Adapter_Abstract $adapter) {
        $config = $adapter->getConfig();
        $message = str_replace(array($config['username'], $config['password']), array('--user--', '--pass--'));
        parent::__construct($message, $code, $previous);
     }         
}

如果环境是开发环境,另一种选择是在检查引导程序后使用 set_exception_handler,然后您可以执行以下操作;

function exception_handler($exception) {
    if ($exception instanceof PDOException) {
        //load your configuration
        //replace the user pass in the message
        //rethrow the exceptio with the updated message
    }
}

set_exception_handler('exception_handler');

我处理不可预见的异常的方式是将它们保存到一个队列中,然后通过电子邮件将所有必要的信息发送给我,我们可以修复异常的原因。

希望有帮助

【讨论】:

    猜你喜欢
    • 2014-04-28
    • 1970-01-01
    • 2011-01-15
    • 2010-12-20
    • 1970-01-01
    • 2017-08-09
    • 1970-01-01
    • 2013-02-21
    • 1970-01-01
    相关资源
    最近更新 更多