Moved from security bug report: "This should be a pubic hardening since there is no direct exploit"

Risk: If someone has
* At least read-only access to wherever php is writing its error logs,
* And can cause Drupal to fail to connect to the database (or can just wait for a network event that causes this),
Then they can find the username and password Drupal is using to connect.

This is with Drupal 8.6.16
I’m fairly certain this is in just Drupal Core, but haven’t actually tried it with just a base install.

If Drupal fails to connect to the database, it logs to the standard error log: (sanitized our database and password info with XXXX)
PDOException: SQLSTATE[HY000] [2002] php_network_getaddresses: getaddrinfo failed: Name or
service not known in /var/www/html/core/lib/Drupal/Component/DependencyInjection/PhpArrayContainer.php on line 79 #0 /var/www/html/core/lib/Drupal/Core/Database/Driver/mysql/Connection.php(420): PDO->__construct('mysql:host=va-v...', 'XXXdevweb', 'XXXXXXXXXXXX', Array)\n#1 /var/www/html/core/lib/Drupal/Core/Database/Database.php(371): Drupal\\Core\\Database\\Driver\\mysql\\Connection::open(Array)\n#2 /var/www/html/core/lib/Drupal/Core/Database/Database.php(166): Drupal\\Core\\Database\\Database::openConnection('default', 'default')\n#3 [internal function]: Drupal\\Core\\Database\\Database::getConnection('default')\n#4 /var/www/html/core/lib/Drupal/Component/DependencyInjection/PhpArrayContainer.php(79): call_user_func_array('Drupal\\\\Core\\\\Dat...', Array)\n#5 /var/www/html/core/lib/Drupal/Component/DependencyInjection/Container.php(171): Drupal\\Component\\DependencyInjection\\PhpArrayContainer->createService(Array, 'database')\n#6 /var/www/html/core/lib/Drupal/Component/DependencyInjection/PhpArrayContainer.php(260): Drupal\\Component\\DependencyInjection\\Container->get('database', 1)\n#7 /var/www/html/core/lib/Drupal/Component/DependencyInjection/PhpArrayContainer.php(62): Drupal\\Component\\DependencyInjection\\PhpArrayContainer->resolveServicesAndParameters(Array)\n#8 /var/www/html/core/lib/Drupal/Component/DependencyInjection/Container.php(171): Drupal\\Component\\DependencyInjection\\PhpArrayContainer->createService(Array, 'cache.container')\n#9 /var/www/html/core/lib/Drupal/Core/DrupalKernel.php(543): Drupal\\Component\\DependencyInjection\\Container->get('cache.container')\n#10 /var/www/html/core/lib/Drupal/Core/DrupalKernel.php(904): Drupal\\Core\\DrupalKernel->getCachedContainerDefinition()\n#11 /var/www/html/core/lib/Drupal/Core/DrupalKernel.php(476): Drupal\\Core\\DrupalKernel->initializeContainer()\n#12 /var/www/html/core/lib/Drupal/Core/DrupalKernel.php(692): Drupal\\Core\\DrupalKernel->boot()\n#13 /var/www/html/index.php(19): Drupal\\Core\\DrupalKernel->handle(Object(Symfony\\Component\\HttpFoundation\\Request))\n#14 {main}

Which includes both the username (XXXdevweb) and password (XXXXXXXXXXXX) in plain text.

The problem is in core/lib/Drupal/Core/Database/Driver/mysql/Connection.php in the catch for line 419. The default error handler prints the sensitive information.
try {
$pdo = new \PDO($dsn, $connection_options['username'], $connection_options['password'], $connection_options['pdo']);
}
catch (\PDOException $e) {
if ($e->getCode() == static::DATABASE_NOT_FOUND) {
throw new DatabaseNotFoundException($e->getMessage(), $e->getCode(), $e);
}
if ($e->getCode() == static::ACCESS_DENIED) {
throw new DatabaseAccessDeniedException($e->getMessage(), $e->getCode(), $e);
}
throw $e;
}

The below fixes the issue, but uses DatabaseAccessDeniedException() incorrectly, and shouldn’t be used, but a new exception handler like it would probably be the correct solution. I just don’t have the time to dig into things to identify what’s the correct resolution, and this works well enough for us.
diff a/core/lib/Drupal/Core/Database/Driver/mysql/Connection.php b/core/lib/Drupal/Core/Database/Driver/mysql/Connection.php
429c429
> throw $e;
---
< throw new DatabaseAccessDeniedException($e->getMessage(), $e->getCode(), $e);

--
Thanks,

Billy Arnold
Senior Systems Administrator
Virginia Interactive / VI

Issue fork drupal-3061567

Command icon Show commands

Start within a Git clone of the project using the version control instructions.

Or, if you do not have SSH keys set up on git.drupalcode.org:

Comments

warnold created an issue. See original summary.

cilefen’s picture

cilefen’s picture

Version: 8.6.x-dev » 8.8.x-dev

Version: 8.8.x-dev » 8.9.x-dev

Drupal 8.8.0-alpha1 will be released the week of October 14th, 2019, which means new developments and disruptive changes should now be targeted against the 8.9.x-dev branch. (Any changes to 8.9.x will also be committed to 9.0.x in preparation for Drupal 9’s release, but some changes like significant feature additions will be deferred to 9.1.x.). For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

Version: 8.9.x-dev » 9.1.x-dev

Drupal 8.9.0-beta1 was released on March 20, 2020. 8.9.x is the final, long-term support (LTS) minor release of Drupal 8, which means new developments and disruptive changes should now be targeted against the 9.1.x-dev branch. For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

Version: 9.1.x-dev » 9.2.x-dev

Drupal 9.1.0-alpha1 will be released the week of October 19, 2020, which means new developments and disruptive changes should now be targeted for the 9.2.x-dev branch. For more information see the Drupal 9 minor version schedule and the Allowed changes during the Drupal 9 release cycle.

Version: 9.2.x-dev » 9.3.x-dev

Drupal 9.2.0-alpha1 will be released the week of May 3, 2021, which means new developments and disruptive changes should now be targeted for the 9.3.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Silicon.Valet made their first commit to this issue’s fork.

Silicon.Valet’s picture

Wrapping the host lookup fail exception does the needfuls (for me anyway). but it does beg the question about why we allow the bare PDOexception to be rethrown when

https://api.drupal.org/api/drupal/core%21lib%21Drupal%21Core%21Database%...
says "This class will always wrap a PDOException."

/me looks at actual code and determines that is a lie

I didn't address that here, because I feel like that probably requires some discussion. But my PR adds the DrupalHostLookupException and catches the case.

Silicon.Valet’s picture

After consulting https://dev.mysql.com/doc/mysql-errors/8.0/en/client-error-reference.html
I see that 2002 is a more general connect failure, so I'm renaming it.

Searching logs I see that 2006 (connection dropped) also leaks credentials, so I'm addressing that in an update to the PR as well.

longwave’s picture

Status: Active » Needs work
Issue tags: +Needs tests

Thanks for working on this!

The testbot doesn't like one of your file permissions:

check failed: file core/lib/Drupal/Core/Database/DatabaseGoneAwayException.php should be 644 not 755

Also I think it would be good if we could add tests for these cases, if possible. Not sure how we could simulate or force "gone away" in a test but we should be able to force a connection failure with some known bad credentials.

bobbygryzynger’s picture

StatusFileSize
new2.54 KB
new2.52 KB
new2.52 KB

Uploading versions of the patch for currently supported versions of Core.

bobbygryzynger’s picture

StatusFileSize
new2.59 KB
new2.56 KB
new2.56 KB

Fixing patch paths.

Silicon.Valet’s picture

Regarding creating tests, does the test setup test against MySQL? These are driver specific errors. I'd also be wary of going down this cascade as there are not tests for the /existing/ thrown exceptions.

longwave’s picture

Core tests run on all our supported database drivers, but individual tests can opt to skip if they are not running on a relevant driver.

\Drupal\FunctionalTests\Bootstrap\UncaughtExceptionTest::testLostDatabaseConnection() already tests for the DatabaseAccessDeniedException case on both MySQL and Postgres, so I think it should be possible to copy and modify this to test DatabaseConnectionErrorException as well? Not sure about DatabaseGoneAwayException though, unless we can do something like set a very short timeout and cause it to be exceeded.

Version: 9.3.x-dev » 9.4.x-dev

Drupal 9.3.0-rc1 was released on November 26, 2021, which means new developments and disruptive changes should now be targeted for the 9.4.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.4.x-dev » 9.5.x-dev

Drupal 9.4.0-alpha1 was released on May 6, 2022, which means new developments and disruptive changes should now be targeted for the 9.5.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

jordanpagewhite’s picture

StatusFileSize
new2.59 KB

Re-rolled for the 9.4.x branch

Version: 9.5.x-dev » 10.1.x-dev

Drupal 9.5.0-beta2 and Drupal 10.0.0-beta2 were released on September 29, 2022, which means new developments and disruptive changes should now be targeted for the 10.1.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

alexdmccabe’s picture

Status: Needs work » Needs review
StatusFileSize
new2.62 KB
new4.15 KB

Re-rolled for 10.0.x. Hopefully this could get committed to 10.0 instead of 10.1.

alexdmccabe’s picture

Got the patch paths wrong.

alexdmccabe’s picture

Okay, never mind, I don't know why this isn't applying.

alexdmccabe’s picture

Should apply, but the interdiff from #19 fails.

jordanpagewhite’s picture

This issue will be resolved for applications running PHP 8.2 due to the #[\SensitiveParameter] attribute being used in the PDO constructor. Please see my comment on https://www.drupal.org/project/drupal/issues/3296293#comment-14872664 for more context.

cilefen’s picture

Status: Needs review » Postponed (maintainer needs more info)

Because PHP 8.2 has a natural and risk-free solution to the problem I suggest we close this as a duplicate of #3296293: Apply SensitiveParameter attribute and move credit there.

solideogloria’s picture

Version: 10.1.x-dev » 11.x-dev

Drupal core is moving towards using a “main” branch. As an interim step, a new 11.x branch has been opened, as Drupal.org infrastructure cannot currently fully support a branch named main. New developments and disruptive changes should now be targeted for the 11.x branch, which currently accepts only minor-version allowed changes. For more information, see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

krisahil’s picture

The error logging for mysql error 2002 (connection refused) has been fixed in https://www.drupal.org/project/drupal/issues/2610858, released in Drupal core 10.0.6.

Here's a re-roll of the current issue's patch that includes error code 2006 (server gone away).

solideogloria’s picture

Status: Postponed (maintainer needs more info) » Closed (duplicate)

I agree that this should be closed as a duplicate of #3296293: Apply SensitiveParameter attribute

krisahil’s picture

Re-rolled patch against Drupal 10.2.

solideogloria’s picture

The patch is not needed in Drupal 10.1+, because credentials are not logged when an exception occurs. Drupal uses the #[\SensitiveParameter] attribute.

If you think the above patch code should still be added, it probably needs to be in another issue, as this issue is closed, and the problem that was reported has already been fixed.

krisahil’s picture

Thanks for pointing that out. However, that attribute only takes effect if the site is hosted on PHP 8.2+ (IIUC). I am maintaining a site on PHP 8.1, so I need this patch, until we upgrade to PHP 8.2

solideogloria’s picture

Ah, that's right. My mistake.