为啥连接失败时 PDO 会打印我的密码?

Posted

技术标签:

【中文标题】为啥连接失败时 PDO 会打印我的密码?【英文标题】:Why does PDO print my password when the connection fails?为什么连接失败时 PDO 会打印我的密码? 【发布时间】:2011-09-21 06:03:20 【问题描述】:

我有一个简单的网站,我使用 PDO 建立与 mysql 服务器的连接。

$dbh = new PDO('mysql:host=localhost;dbname=DB;port=3306',
               'USER',
               'SECRET', 
               array(PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8"));

我的网站有一些流量,服务器的连接数已达到限制,网站抛出此错误,其中包含我的普通密码!

致命错误:未捕获的异常 带有消息的“PDOException” 'SQLSTATE [08004] [1040] 太多 连接'在 /home/domain/html/index.php:xxx 堆栈跟踪:#0 /home/domain/html/index.php(64): PDO->__construct('mysql:host=local...', “用户”、“秘密”、数组)#1 main 投入 /home/domain/html/index.php 上 第 64 行

具有讽刺意味的是,出于安全原因,我切换到 PDO,所以这真的让我感到震惊,因为在大多数网站上,使用简单的 HTTP 泛洪很容易引发这个确切的错误。

我现在已经将我的连接包装在一个 try/catch 块中,但我仍然认为这是灾难性的!

我是 PDO 的新手,所以我的问题是:我必须做些什么才能确保安全?如何以安全的方式建立连接?是否还有其他我必须注意的已知安全漏洞?

【问题讨论】:

请参阅:***.com/questions/5811834/… 使用动态表/db/列名时的漏洞,以及如何堵塞该漏洞。 我完全同意关闭生产中的错误、try/catch 和类似的东西,但请考虑您是否有一个离岸的程序员“团队”,其中“初级”程序员不应该知道密码,正如你所说,这是一个“灾难性”的安全漏洞。更不用说,根本不关心关闭错误的新手程序员。话虽如此,我对这个在错误时透露密码的决定感到困惑。 天哪,这病了!我的天啊!这绝对令人发指!你需要更多的支持来保持冷静,而不是进入 CAPS RAGE。 可能重复:Uncaught PDOException reveals username and password 这些答案都没有提到的是,即使让这些信息接触日志也是一种不好的做法(参见OWASP logging cheat sheet)。因此,您不想简单地捕获异常、给出通用消息并记录真正的 PDO 消息,因为这样凭据就在您的日志中。至少在记录之前做一个str_replace 来编辑该信息。 【参考方案1】:

无论如何,您的 PHP.ini 中应该有 display_errors = off 以避免这个问题。除了 PDO 之外,揭示此类细节的错误还来自许多地方。

是的,您还应该将它放在 try/catch 块中。

您也可以$pdo->setAttribute(PDO::ERRMODE_SILENT),但您需要手动检查错误代码,而不是使用 try/catch 块。有关更多错误常量,请参阅http://php.net/manual/en/pdo.setattribute.php。

【讨论】:

@Joe,那么您应该与 PDO 开发人员讨论。我认为它从堆栈中返回这样的信息没有问题。一旦你意识到它,这不是问题。当然,在某些应用程序中,try/catch 会被遗忘……这对某些人来说不可避免地会成为问题……你说得对。 这两个建议似乎都不适合我。当连接失败时,错误堆栈会打印到屏幕上,并且可以看到纯文本密码。我正在使用 PHP ActiveRecord。【参考方案2】:

好吧,这让我有点傻笑。错误报告的用途是用于调试目的,它可以让您快速找到并修复问题。

当您在实时环境中时,您的服务器应配置为仅用于内部日志记录,而不是直接输出,因此基本上您需要关闭 php.ini 中的错误输出。

display_errors = Off

但是当您在测试环境中时,此堆栈只是一个帮助您的工具,并且是可配置的。

当实时环境中发生错误时,它们会被记录下来,因此您应该始终检查您的日志文件,然后进行相应的修复。

人们可能会指定您可以在 PHP 应用程序中管理错误,但根据个人喜好,我认为这是错误的做法。为您的网络服务器和 MySQL / SQL Server 配置 INI 和配置文件将导致更敏锐的管理。

如果您的应用程序是公共应用程序,那么处理应用程序内的错误也是一个好主意,因为大部分客户端可能在共享主机上并且没有对服务器配置的完全访问权限。

【讨论】:

【参考方案3】:

一个简单的解决方法是捕获 PDO 构造函数抛出的 PDOException:

try 
    $dbh  =  new PDO('mysql:host=localhost;dbname=DB;port=3306', 'USER',
    'SECRET', array(PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8"));
 catch (PDOException $e) 
    throw new Exception($e->getMessage());

【讨论】:

...这会让开发人员没有错误消息,否则会帮助他们解决问题。 出现异常“无法连接到数据库”是否让开发人员一无所知?我不这么认为 基本概念不好。任何人都不应该将纯密码放入对象并将其保存在内存中。正确的解决方案是更改 PDO 代码,而不是将普通密码放入异常消息中。我认为这是 PDO 本身的错误和安全漏洞。 @YourCommonSense 这很容易修复(我已经在编辑中包含了如何修复)。 @duskwuff 非常感谢。它确实解决了一个问题。我已经更新了我所有的教程。我唯一要补充的是,如果一个函数用于包装连接代码(通常是这样),它不应该接受原始凭据作为其参数,而是接受它们作为数组或对象或在身体。否则问题将被上移一级但仍然存在。【参考方案4】:

我们使用编码的用户名和密码,并在 PDO 构造函数中对其进行解码。然后我们捕获 PDOException 并抛出一个新的 PDOException 和旧异常的消息,这样跟踪将只显示编码的用户名和密码。

一个好的PHP加密库是defuse/php-encryption。

示例代码:

<?php
class myPDOWrapper extends PDO
    

        public function __construct(string $dns, string $encodedUser, string $encodedPassword)
        
            try 
                parent::__construct($dns, $this->decodeFunction($encodedUser), $this->decodeFunction($encodedPassword),
                    [
                        PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
                    ]
                );
            
            catch (PDOException $exception) 
                throw new PDOException($exception->getMessage());
            
        

        private function decodeFunction(string $encoded): string
        
            return \Defuse\Crypto\Crypto::decrypt($encoded, $this->decodeKey());
        

        private function decodeKey(): \Defuse\Crypto\Key
        
            static $key = null;

            if(null === $key) 
                $key = \Defuse\Crypto\Key::loadFromAsciiSafeString(getenv('MY_PDO_DECODE_KEY'));
            

            return $key;
        
    

【讨论】:

以上是关于为啥连接失败时 PDO 会打印我的密码?的主要内容,如果未能解决你的问题,请参考以下文章

为啥mysql PDO不会在失败时抛出错误[重复]

黄聪:PHP数据库连接失败--could not find driver 解决办法

黄色仓库为啥显示连接失败

为啥ERP的交换数据库连接测试总是失败

为啥我在使用 JDBC 和 MySQL 的 JSP 中出现连接失败错误

为啥手机登录QQ邮箱总是显示连接服务器失败?