Почему PDO печатает мой пароль при сбое соединения?

У меня есть простой сайт, на котором я устанавливаю соединение с сервером Mysql с использованием PDO.

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

У меня был некоторый трафик на моем сайте, и был достигнут лимит подключения к серверу, и сайт выдает эту ошибку с моим паролем PLAIN!

Неустранимая ошибка: исключить исключение «PDOException» с сообщением «SQLSTATE [08004] [1040] Слишком много соединений» в /home/domain/html/index.php:xxx Трассировка стека: # 0 /home/domain/html/index.php (64): PDO -> __ construct ('mysql: host = loca …', 'USER', 'SECRET', Array) # 1 {main} брошен в /home/domain/html/index.php в строке 64

По иронии судьбы я переключился на PDO по соображениям безопасности, так что это действительно шокировало меня, потому что эта точная ошибка – это то, что вы можете очень легко спровоцировать на большинстве сайтов, используя простой HTTP-наводнение.

Теперь я завернул соединение в блок try / catch, но все же считаю, что это катастрофично!

Я новичок в PDO, и поэтому мой вопрос: что я должен сделать, чтобы считать безопасным? Как установить соединение безопасным способом? Существуют ли другие известные дыры в безопасности, подобные этой, о которой я должен знать?

Вы должны иметь display_errors = off в вашем PHP.ini в любом случае, чтобы избежать этой проблемы. Ошибки, которые раскрывают такие данные, поступают из многих мест, в дополнение к PDO.

Да, вы также должны иметь его в блоке try / catch.

Вы также можете $pdo->setAttribute(PDO::ERRMODE_SILENT) , но тогда вам нужно вручную проверять коды ошибок, а не использовать блок try / catch. См. http://php.net/manual/en/pdo.setattribute.php для получения дополнительных констант ошибок.

Хорошо, это заставило меня немного хихикать, использование отчетов об ошибках предназначено для отладки, это позволяет быстро находить и исправлять проблемы.

Когда вы находитесь в живой среде, ваш сервер должен быть настроен только для внутреннего ведения журнала, а не для прямого вывода, поэтому в основном вам нужно будет отключить вывод ошибок в вашем php.ini .

 display_errors = Off 

Но пока вы находитесь в тестовой среде, этот стек – это просто инструмент, который поможет вам и настраивается.

Когда ошибки происходят в живой среде, они будут регистрироваться, поэтому вы всегда должны проверять свои файлы журналов, а затем исправлять их соответствующим образом.

Люди могут указать, что вы можете управлять ошибками в своем PHP-приложении, но по личным предпочтениям, я думаю, что это неправильный способ сделать это, настройка INI и файлов конфигурации для вашего веб-сервера и MySQL / MsSQL приведет к более острому управлению.

Если ваше приложение является общедоступным, тогда было бы неплохо обработать ошибки в приложении, так как большой процент клиентов может находиться на общем хостинге и не иметь полного доступа к конфигурациям сервера.

Простой обходной путь, чтобы поймать PDOException, созданный конструктором PDO:

 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('Could not connect to database'); } 

Мы используем закодированное имя пользователя и пароли и декодируем их в конструкторе PDO, затем мы улавливаем PDOException и бросаем новое исключение PDOException со старым исключением, его сообщение, так что на трассе будет отображаться только кодированное имя пользователя и пароль.

Хорошей библиотекой шифрования для PHP является: defuse / php-encryption

https://github.com/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; } }