Почему 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 → __ конструкт ( 'MySQL: хост = Сумасшедшая...', 'USER', 'SECRET', Array) # 1 {main} брошен /home/domain/html/index.php on строка 64
По иронии судьбы я переключился на PDO по соображениям безопасности, так что это действительно шокировало меня, потому что эта точная ошибка - это то, что вы можете очень легко спровоцировать на большинстве сайтов, используя простую HTTP-атаку.
Теперь я завернул свое соединение в блок try/catch, но все же считаю, что это катастрофично!
Я новичок в PDO, и поэтому мой вопрос: что я должен сделать, чтобы считать безопасным? Как установить соединение безопасным способом? Существуют ли другие известные дыры в безопасности, подобные этой, о которой я должен знать?
Ответы
Ответ 1
Вы должны иметь display_errors = off
в вашем PHP.ini в любом случае, чтобы избежать этой проблемы. Ошибки, которые раскрывают такие данные, поступают из многих мест, в дополнение к PDO.
Да, вы также должны иметь его в блоке try/catch.
Вы также можете $pdo->setAttribute(PDO::ERRMODE_SILENT)
, но тогда вам нужно вручную проверять коды ошибок, а не использовать блок try/catch. См. http://php.net/manual/en/pdo.setattribute.php для получения дополнительных констант ошибок.
Ответ 2
Хорошо, это заставило меня немного хихикать, использование отчетов об ошибках предназначено для целей отладки, это позволяет быстро находить и исправлять проблемы.
Когда вы находитесь в живой среде, ваш сервер должен быть настроен только для внутреннего ведения журнала, а не для прямого вывода, поэтому в основном вам нужно будет отключить вывод ошибок в вашем php.ini
.
display_errors = Off
Но пока вы находитесь в своей тестовой среде, этот стек - это просто инструмент, который поможет вам и настраивается.
При возникновении ошибок в живой среде они будут регистрироваться, поэтому вы всегда должны проверять свои файлы журналов, а затем исправлять их.
Люди могут указывать, что вы можете управлять ошибками в своем PHP-приложении, но по личным предпочтениям я думаю, что это неправильный путь для этого, настройка INI и файлов конфигурации для вашего веб-сервера и MySQL/MsSQL приведет к большему количеству острое управление.
Если ваше приложение является общедоступным приложением, тогда было бы неплохо обрабатывать ошибки в приложении, так как большой процент клиентов может находиться на общем хостинге и не иметь полного доступа к конфигурациям сервера.
Ответ 3
Простой обходной путь, чтобы поймать 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');
}
Ответ 4
Мы используем закодированное имя пользователя и пароли и декодируем их в конструкторе 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;
}
}